diff options
Diffstat (limited to 'vid-webpack-master/src')
102 files changed, 3412 insertions, 1261 deletions
diff --git a/vid-webpack-master/src/app/app.routing.ts b/vid-webpack-master/src/app/app.routing.ts index 779d17e3c..eaf4e9ca4 100644 --- a/vid-webpack-master/src/app/app.routing.ts +++ b/vid-webpack-master/src/app/app.routing.ts @@ -6,12 +6,20 @@ import {SupportComponent} from "./support/support.component"; import {HealthStatusRoutes} from "./healthStatus/health-status.routing"; import {VlanTaggingRoutes} from "./vlanTagging/vlan-tagging.routing"; import {InstantiationStatusRoutes} from "./instantiationStatus/InstantiationStatus.routing"; +import {InstantiationTemplatesModalComponent} from "./shared/components/genericFormPopup/instantiationTemplatesModal/instantiation.templates.modal.component"; const routes: Routes = [ ...DrawingBoardRoutes, ...HealthStatusRoutes, ...VlanTaggingRoutes, ...InstantiationStatusRoutes, { + path: 'instantiationTemplatesPopup', + component: InstantiationTemplatesModalComponent, + resolve: { + flags: FlagsResolve + } + }, + { path: 'servicePopup', component: GenericFormPopupComponent, resolve: { diff --git a/vid-webpack-master/src/app/drawingBoard/drawingBoard.routing.ts b/vid-webpack-master/src/app/drawingBoard/drawingBoard.routing.ts index 0c1fa700c..aebbdee5a 100644 --- a/vid-webpack-master/src/app/drawingBoard/drawingBoard.routing.ts +++ b/vid-webpack-master/src/app/drawingBoard/drawingBoard.routing.ts @@ -4,6 +4,7 @@ import {FlagsResolve} from "../shared/resolvers/flag/flag.resolver"; import {ViewEditResolver} from "../shared/resolvers/viewEdit/viewEdit.resolver"; import {DrawingBoardGuard} from "./guards/servicePlanningGuard/drawingBoardGuard"; import {RetryResolver} from "../shared/resolvers/retry/retry.resolver"; +import {RecreateResolver} from "../shared/resolvers/recreate/recreate.resolver"; export const DrawingBoardRoutes: Route[] = [ { @@ -27,6 +28,14 @@ export const DrawingBoardRoutes: Route[] = [ } }, { + path: 'RECREATE', + component: ServicePlanningComponent, + resolve: { + flags: FlagsResolve, + viewEditResolver: RecreateResolver + } + }, + { path: 'RETRY_EDIT', component: ServicePlanningComponent, resolve: { diff --git a/vid-webpack-master/src/app/drawingBoard/service-planning/available-models-tree/available-models-tree.component.ts b/vid-webpack-master/src/app/drawingBoard/service-planning/available-models-tree/available-models-tree.component.ts index 145ee19da..fb1172945 100644 --- a/vid-webpack-master/src/app/drawingBoard/service-planning/available-models-tree/available-models-tree.component.ts +++ b/vid-webpack-master/src/app/drawingBoard/service-planning/available-models-tree/available-models-tree.component.ts @@ -28,6 +28,7 @@ import {DrawingBoardTreeComponent} from "../drawing-board-tree/drawing-board-tre import {ComponentInfoModel} from "../component-info/component-info-model"; import {ComponentInfoService} from "../component-info/component-info.service"; import {FeatureFlagsService, Features} from "../../../shared/services/featureFlag/feature-flags.service"; +import {Utils} from "../../../shared/utils/utils"; @Component({ @@ -93,6 +94,7 @@ export class AvailableModelsTreeComponent { service = {name: ''}; options: ITreeOptions = { + allowDrop:false, nodeHeight: 36, dropSlotHeight: 0, nodeClass: (node: ITreeNode) => { @@ -145,10 +147,10 @@ export class AvailableModelsTreeComponent { this.isNewObject = isNewObject; let data = node.data; let dynamicInputs = data.dynamicInputs; - let isAlaCarte: boolean = this.serviceHierarchy.service.vidNotions.instantiationType == 'ALaCarte'; + let isAlaCarte: boolean = Utils.isALaCarte(this.serviceHierarchy.service.vidNotions.instantiationType); let isEcompGeneratedNaming: boolean = data.isEcompGeneratedNaming; let type: string = data.type; - if (!this.store.getState().global.flags['FLAG_SETTING_DEFAULTS_IN_DRAWING_BOARD'] || node.data.type === ServiceNodeTypes.VF || + if (node.data.type === ServiceNodeTypes.VF || this._availableModelsTreeService.shouldOpenDialog(type, dynamicInputs, isEcompGeneratedNaming)) { this._iframeService.addClassOpenModal(this.parentElementClassName); node.data.onAddClick(node, serviceId); diff --git a/vid-webpack-master/src/app/drawingBoard/service-planning/drawing-board-header/drawing-board-header.component.ts b/vid-webpack-master/src/app/drawingBoard/service-planning/drawing-board-header/drawing-board-header.component.ts index 7923313bf..8228f9531 100644 --- a/vid-webpack-master/src/app/drawingBoard/service-planning/drawing-board-header/drawing-board-header.component.ts +++ b/vid-webpack-master/src/app/drawingBoard/service-planning/drawing-board-header/drawing-board-header.component.ts @@ -180,7 +180,7 @@ export class DrawingBoardHeader { } } - extractOwningEntityNameAccordingtoId(id:String): string { + extractOwningEntityNameAccordingToId(id:String): string { let owningEntityName; _.forEach(this.store.getState().service.categoryParameters.owningEntityList,(owningEntity: OwningEntity) => { if (owningEntity.id === id) { @@ -190,12 +190,20 @@ export class DrawingBoardHeader { return owningEntityName; } + private extractSubscriberNameByGlobalSubscriberId(globalSubscriberId: string) { + return this.store.getState().service.subscribers.find(sub => sub.id === globalSubscriberId).name; + } + extractServiceFields(): any { let instanceFields : ServiceInstance; instanceFields = this.store.getState().service.serviceInstance[this.serviceModelId]; if (instanceFields.action === ServiceInstanceActions.Create) { - instanceFields.subscriberName = this.store.getState().service.subscribers.find(sub => sub.id === instanceFields.globalSubscriberId).name; - instanceFields.owningEntityName = this.extractOwningEntityNameAccordingtoId(instanceFields.owningEntityId); + if(_.isNil(instanceFields.subscriberName)) { + instanceFields.subscriberName = this.extractSubscriberNameByGlobalSubscriberId(instanceFields.globalSubscriberId); + } + if (_.isNil(instanceFields.owningEntityName)) { + instanceFields.owningEntityName = this.extractOwningEntityNameAccordingToId(instanceFields.owningEntityId); + } } return _.omit(instanceFields,['optionalGroupMembersMap', 'upgradedVFMSonsCounter', 'isUpgraded', 'latestAvailableVersion']); } diff --git a/vid-webpack-master/src/app/drawingBoard/service-planning/drawing-board-header/drawing-board-header.service.ts b/vid-webpack-master/src/app/drawingBoard/service-planning/drawing-board-header/drawing-board-header.service.ts index 1b71d9098..634fa6271 100644 --- a/vid-webpack-master/src/app/drawingBoard/service-planning/drawing-board-header/drawing-board-header.service.ts +++ b/vid-webpack-master/src/app/drawingBoard/service-planning/drawing-board-header/drawing-board-header.service.ts @@ -96,7 +96,7 @@ export class DrawingBoardHeaderService{ showEditService(mode: DrawingBoardModes, serviceModelId: string): boolean{ const serviceInstance = this.store.getState().service.serviceInstance; - return mode === DrawingBoardModes.CREATE || ((mode === DrawingBoardModes.RETRY_EDIT || mode === DrawingBoardModes.EDIT)&& + return mode === DrawingBoardModes.CREATE || ((mode === DrawingBoardModes.RETRY_EDIT || mode === DrawingBoardModes.EDIT || mode === DrawingBoardModes.RECREATE )&& !_.isNil(serviceInstance) && !_.isNil(serviceInstance[serviceModelId])&& serviceInstance[serviceModelId].action === ServiceInstanceActions.Create); } diff --git a/vid-webpack-master/src/app/drawingBoard/service-planning/drawing-board-tree/dragAndDrop/dragAndDrop.service.spec.ts b/vid-webpack-master/src/app/drawingBoard/service-planning/drawing-board-tree/dragAndDrop/dragAndDrop.service.spec.ts index 01ae898f5..425568b68 100644 --- a/vid-webpack-master/src/app/drawingBoard/service-planning/drawing-board-tree/dragAndDrop/dragAndDrop.service.spec.ts +++ b/vid-webpack-master/src/app/drawingBoard/service-planning/drawing-board-tree/dragAndDrop/dragAndDrop.service.spec.ts @@ -5,14 +5,15 @@ import {DragAndDropService} from "./dragAndDrop.service"; import {AppState} from "../../../../shared/store/reducers"; class MockAppStore<T> { - dispatch(){ + dispatch() { } + getState() { return { global: { flags: { - "FLAG_1911_INSTANTIATION_ORDER_IN_ASYNC_ALACARTE" : true + "FLAG_1911_INSTANTIATION_ORDER_IN_ASYNC_ALACARTE": true } }, service: { @@ -53,6 +54,7 @@ describe('Drag and drop service', () => { let service: DragAndDropService; let httpMock: HttpTestingController; let store: NgRedux<AppState>; + let nodes; beforeAll(done => (async () => { TestBed.configureTestingModule({ @@ -67,158 +69,142 @@ describe('Drag and drop service', () => { service = injector.get(DragAndDropService); httpMock = injector.get(HttpTestingController); store = injector.get(NgRedux); + + })().then(done).catch(done.fail)); + beforeEach(() => { + nodes = [ + { + "trackById": "ckfqe3sb3y8", + "componentInfoType": "VNF", + "parentType": "", + "type": "VF", + "typeName": "VNF", + "instanceName": "2017-488_PASQUALE-vPE", + "id": "04686zg11ur2", + "children": [ + { + "id": "1150884479608", + "action": "Create", + "instanceName": "puwesovabe", + "name": "puwesovabe", + "type": "VFmodule", + "trackById": "d5if1906rqa", + "parentType": "VNF", + "position": 1, + "componentInfoType": "VFModule", + "errors": {}, + "updatePoistionFunction": () => { + }, + }, + { + "id": "4637423092446", + "action": "Create", + "instanceName": "bnmgtrx", + "name": "bnmgtrx", + "type": "VFmodule", + "trackById": "9ei9adlh27e", + "parentType": "VNF", + "position": 2, + "componentInfoType": "VFModule", + "updatePoistionFunction": () => { + } + } + ], + "errors": {}, + } + ]; + }) + test('drag should execute array_move when the nodes parent are same', () => { - test('drag should move element position', () => { - let nodes = [{ - "modelCustomizationId": "91415b44-753d-494c-926a-456a9172bbb9", - "modelId": "d6557200-ecf2-4641-8094-5393ae3aae60", - "modelUniqueId": "91415b44-753d-494c-926a-456a9172bbb9", - "missingData": false, - "id": "tjjongy92jn", - "action": "Create", - "inMaint": false, - "name": "yoav2_001", - "modelName": "VF_vGeraldine 0", - "type": "VF", - "isEcompGeneratedNaming": true, - "networkStoreKey": "VF_vGeraldine 0:0001", - "vnfStoreKey": "VF_vGeraldine 0:0001", - "typeName": "VNF", - "menuActions": {"edit": {}, "showAuditInfo": {}, "duplicate": {}, "remove": {}, "delete": {}, "undoDelete": {}}, - "isFailed": false, - "statusProperties": [{"key": "Prov Status:", "testId": "provStatus"}, { - "key": "Orch Status:", - "testId": "orchStatus" - }], - "trackById": "di9khuolht", - "parentType": "", - "position": 0, - "children": [{ - "modelCustomizationId": "f8c040f1-7e51-4a11-aca8-acf256cfd861", - "modelId": "a27f5cfc-7f12-4f99-af08-0af9c3885c87", - "modelUniqueId": "f8c040f1-7e51-4a11-aca8-acf256cfd861", - "missingData": false, - "id": 6654971919519, - "action": "Create", - "name": "VFModule1", - "modelName": "vf_vgeraldine0..VfVgeraldine..base_vflorence..module-0", - "type": "VFmodule", - "isEcompGeneratedNaming": true, - "dynamicInputs": [], - "dynamicModelName": "vf_vgeraldine0..VfVgeraldine..base_vflorence..module-0bykqx", - "typeName": "M", - "menuActions": {"edit": {}, "showAuditInfo": {}, "remove": {}, "delete": {}, "undoDelete": {}}, - "isFailed": false, - "statusProperties": [{"key": "Prov Status:", "testId": "provStatus"}, { - "key": "Orch Status:", - "testId": "orchStatus" - }], - "trackById": "5pfyfah820h", - "parentType": "VNF", - "position": 0, - "errors": {} - }, { - "modelCustomizationId": "6add59e0-7fe1-4bc4-af48-f8812422ae7c", - "modelId": "41708296-e443-4c71-953f-d9a010f059e1", - "modelUniqueId": "6add59e0-7fe1-4bc4-af48-f8812422ae7c", - "missingData": false, - "id": 987761655742, - "action": "Create", - "name": "VNFModule3", - "modelName": "vf_vgeraldine0..VfVgeraldine..vflorence_gpb..module-2", - "type": "VFmodule", - "isEcompGeneratedNaming": true, - "dynamicInputs": [], - "dynamicModelName": "vf_vgeraldine0..VfVgeraldine..vflorence_gpb..module-2fjrrc", - "typeName": "M", - "menuActions": {"edit": {}, "showAuditInfo": {}, "remove": {}, "delete": {}, "undoDelete": {}}, - "isFailed": false, - "statusProperties": [{"key": "Prov Status:", "testId": "provStatus"}, { - "key": "Orch Status:", - "testId": "orchStatus" - }], - "trackById": "i3dllio31bb", - "parentType": "VNF", - "position": 1, - "errors": {} - }, { - "modelCustomizationId": "55b1be94-671a-403e-a26c-667e9c47d091", - "modelId": "522159d5-d6e0-4c2a-aa44-5a542a12a830", - "modelUniqueId": "55b1be94-671a-403e-a26c-667e9c47d091", - "missingData": false, - "id": 873798901625, - "action": "Create", - "name": "VFModule2", - "modelName": "vf_vgeraldine0..VfVgeraldine..vflorence_vlc..module-1", - "type": "VFmodule", - "isEcompGeneratedNaming": true, - "dynamicInputs": [], - "dynamicModelName": "vf_vgeraldine0..VfVgeraldine..vflorence_vlc..module-1djjni", - "typeName": "M", - "menuActions": {"edit": {}, "showAuditInfo": {}, "remove": {}, "delete": {}, "undoDelete": {}}, - "isFailed": false, - "statusProperties": [{"key": "Prov Status:", "testId": "provStatus"}, { - "key": "Orch Status:", - "testId": "orchStatus" - }], - "trackById": "w7bvw1nh47s", - "parentType": "VNF", - "position": 2, - "errors": {} - }], - "errors": {} - }, { - "modelCustomizationId": "91415b44-753d-494c-926a-456a9172bbb9", - "modelId": "d6557200-ecf2-4641-8094-5393ae3aae60", - "modelUniqueId": "91415b44-753d-494c-926a-456a9172bbb9", - "missingData": false, - "id": "dywch8hkomi", - "action": "Create", - "inMaint": false, - "name": "yoav2", - "modelName": "VF_vGeraldine 0", - "type": "VF", - "isEcompGeneratedNaming": true, - "networkStoreKey": "VF_vGeraldine 0", - "vnfStoreKey": "VF_vGeraldine 0", - "typeName": "VNF", - "menuActions": {"edit": {}, "showAuditInfo": {}, "duplicate": {}, "remove": {}, "delete": {}, "undoDelete": {}}, - "isFailed": false, - "statusProperties": [{"key": "Prov Status:", "testId": "provStatus"}, { - "key": "Orch Status:", - "testId": "orchStatus" - }], - "trackById": "fjczf1urdqo", - "parentType": "", - "position": 1, - "children": [], - "errors": {} - }]; let from = { + id: "04686zg11ur2", + index: 0, data: { - type: 'VF', - index: 1 + instanceName: 'puwesovabe', + }, + parent: { + data: { + type: 'VF', + index: 0, + trackById: 'ckfqe3sb3y8', + vnfStoreKey: '2017-488_PASQUALE-vPE 0', + } } }; let to = { parent: { + id: "4637423092446", + index: 1, data: { - type: 'VF', - index: 0 + instanceName: 'bnmgtrx', + }, + parent: { + data: { + type: 'VF', + trackById: 'ckfqe3sb3y8', + vnfStoreKey: '2017-488_PASQUALE-vPE 0', + } } } }; - jest.spyOn(service, 'array_move'); - service.drag(store, "serviceInstanceId", nodes, {from, to}); + jest.spyOn(service, 'array_move'); + service.drop(store, "serviceInstanceId", nodes, {from, to}); expect(service.array_move).toHaveBeenCalled(); }); + test('drag shouldnt execute array_move when the nodes parent are different', () => { + + let from = { + id: 1150884479608, + index: 0, + data: { + instanceName: '2017-488_PASQUALE-vPE', + }, + parent: {} + }; + + let to = { + parent: { + id: 4637423092446, + index: 1, + data: { + instanceName: 'bnmgtrx', + }, + parent: { + data: { + type: 'VF', + trackById: '1111', + vnfStoreKey: '2017-488_PASQUALE-vPE 0', + } + } + } + }; + + + jest.spyOn(service, 'array_move'); + + service.drop(store, "serviceInstanceId", nodes, {from, to}); + + jest.clearAllMocks(); + + expect(service.array_move).not.toHaveBeenCalled(); + + }); + + test('drop should change nodes index and position', () => { + + let arr: Array<any> = service.array_move(nodes[0].children, 0, 1, "serviceInstanceId", '') + + expect(arr[0]).toMatchObject({instanceName: "bnmgtrx", position: 1}); + expect(arr[1]).toMatchObject({instanceName: "puwesovabe", position: 2}); + + }); }); diff --git a/vid-webpack-master/src/app/drawingBoard/service-planning/drawing-board-tree/dragAndDrop/dragAndDrop.service.ts b/vid-webpack-master/src/app/drawingBoard/service-planning/drawing-board-tree/dragAndDrop/dragAndDrop.service.ts index 15da89ad3..96e50178b 100644 --- a/vid-webpack-master/src/app/drawingBoard/service-planning/drawing-board-tree/dragAndDrop/dragAndDrop.service.ts +++ b/vid-webpack-master/src/app/drawingBoard/service-planning/drawing-board-tree/dragAndDrop/dragAndDrop.service.ts @@ -8,11 +8,29 @@ import * as _ from 'lodash'; @Injectable() export class DragAndDropService { - constructor(private store: NgRedux<AppState>){} + constructor(private store: NgRedux<AppState>) { + } - isAllow(): boolean { + isFlagOn(): boolean { return FeatureFlagsService.getFlagState(Features.FLAG_1911_INSTANTIATION_ORDER_IN_ASYNC_ALACARTE, this.store); } + + + /*********************************************************************************************** + if the falg is ON and nodes have same parent + ***********************************************************************************************/ + isAllowDrop(from: any, to: any): boolean { + return this.isFlagOn() && this.isSameParent(from, to); + } + + private isSameParent(from: any, to: any): boolean { + try { + return from.parent.data.trackById === to.parent.parent.data.trackById; + } catch (e) { //parent not found + return false; + } + } + /******************************************************************** * manage drawing-board drag and drop operation * @param nodes - array with elements data. @@ -22,57 +40,67 @@ export class DragAndDropService { * @param to - element to information ************************************************************/ - drag(store, instanceId : string , nodes, {from, to}) :void{ - if (!store.getState().global.flags["FLAG_1911_INSTANTIATION_ORDER_IN_ASYNC_ALACARTE"]) return; + drop(store, instanceId: string, nodes, {from, to}): void { + + if (!this.isFlagOn()) return; - let firstLevelNames : DragAndDropModel[] = [ + if (this.isAllowDrop(from, to)) { + let vfModules = nodes.find((parent) => { + return parent.trackById === to.parent.parent.data.trackById; + }).children; + this.array_move(vfModules, from.index, to.parent.index, instanceId, to.parent.parent.data.vnfStoreKey); + } + + /* let firstLevelNames : DragAndDropModel[] = [ new DragAndDropModel('VF',true), new DragAndDropModel('VL',true), new DragAndDropModel('VFmodule',false) - ]; - - const fromObject = _.find(firstLevelNames, ['type', from.data.type]); - const toObject = _.find(firstLevelNames, ['type', to.parent.data.type]); - - /*********************************************************************************************** - if the type are the same and there in same level + same parent -> then change element position - ***********************************************************************************************/ - if(fromObject.isFirstLevel === toObject.isFirstLevel){ // moving element in the same level and in the first level - if(fromObject.isFirstLevel){ - this.array_move(nodes, from.index , to.parent.index, instanceId); - } else if(fromObject.isFirstLevel === toObject.isFirstLevel){ - /* check if they have the same parent */ - if(from.parent.data.trackById === to.parent.parent.data.trackById){ - let vfModules = nodes.find((parents)=> { - return parents.trackById === to.parent.parent.data.trackById; - }).children; - this.array_move(vfModules, from.index , to.parent.index, instanceId, to.parent.parent.data.vnfStoreKey); + ]; + + const fromObject = _.find(firstLevelNames, ['type', from.data.type]); + const toObject = _.find(firstLevelNames, ['type', to.parent.data.type]); + + /!*********************************************************************************************** + if the type are the same and there in same level + same parent -> then change element position + ***********************************************************************************************!/ + if(fromObject.isFirstLevel === toObject.isFirstLevel){ // moving element in the same level and in the first level + if(fromObject.isFirstLevel){ + this.array_move(nodes, from.index , to.parent.index, instanceId); + } else if(fromObject.isFirstLevel === toObject.isFirstLevel){ + /!* check if they have the same parent *!/ + if(from.parent.data.trackById === to.parent.parent.data.trackById){ + let vfModules = nodes.find((parents)=> { + return parents.trackById === to.parent.parent.data.trackById; + }).children; + this.array_move(vfModules, from.index , to.parent.index, instanceId, to.parent.parent.data.vnfStoreKey); + } } - } - } + }*/ } - /******************************************************************** + /******************************************************************** * move element inside array with elements position * @param arr - array with elements data. * @param originalPosition - element original position * @param destPosition - element dest position * @param destPinstanceIdosition - instance id ******************************************************************/ - array_move(arr, originalPosition, destPosition, instanceId : string, parentStoreKey?) { - if (destPosition >= arr.length) { - let k = destPosition - arr.length + 1; - while (k--) { - arr.push(undefined); - } - } - arr.splice(destPosition, 0, arr.splice(originalPosition, 1)[0]); + array_move(arr, originalPosition, destPosition, instanceId: string, parentStoreKey?): Array<any> { + + let moved_node = arr[originalPosition] + + arr.splice(originalPosition, 1); + + arr.splice(destPosition, 0, moved_node); + arr.forEach((item, index) => { - if(item.position !== index){ - item.position = index; + if (item.position !== index + 1) { + item.position = index + 1; item.updatePoistionFunction(this, item, instanceId, parentStoreKey); } }); + + return arr; }; } diff --git a/vid-webpack-master/src/app/drawingBoard/service-planning/drawing-board-tree/drawing-board-tree.component.ts b/vid-webpack-master/src/app/drawingBoard/service-planning/drawing-board-tree/drawing-board-tree.component.ts index 0e2d8e276..f3542573d 100644 --- a/vid-webpack-master/src/app/drawingBoard/service-planning/drawing-board-tree/drawing-board-tree.component.ts +++ b/vid-webpack-master/src/app/drawingBoard/service-planning/drawing-board-tree/drawing-board-tree.component.ts @@ -127,11 +127,11 @@ export class DrawingBoardTreeComponent implements OnInit, AfterViewInit { nodes = []; serviceModelId: string; options = { - allowDrag: this._dragAndDropService.isAllow(), + allowDrag: this._dragAndDropService.isFlagOn(), actionMapping: { mouse: { drop: (tree:TreeModel, node:TreeNode, $event:any, {from, to}) => { - this._dragAndDropService.drag(this.store, this.serviceModelId, this.nodes, {from, to}); + this._dragAndDropService.drop(this.store, this.serviceModelId, this.nodes, {from, to}); } } }, diff --git a/vid-webpack-master/src/app/drawingBoard/service-planning/drawing-board.modes.ts b/vid-webpack-master/src/app/drawingBoard/service-planning/drawing-board.modes.ts index 452666f91..78e2b629e 100644 --- a/vid-webpack-master/src/app/drawingBoard/service-planning/drawing-board.modes.ts +++ b/vid-webpack-master/src/app/drawingBoard/service-planning/drawing-board.modes.ts @@ -4,5 +4,6 @@ export enum DrawingBoardModes { VIEW = 'VIEW', EDIT = 'EDIT', OLD_VIEW_EDIT = 'OLD_VIEW_EDIT', - CREATE = 'CREATE' + CREATE = 'CREATE', + RECREATE = 'RECREATE' } diff --git a/vid-webpack-master/src/app/drawingBoard/service-planning/duplicate/duplicate.service.spec.ts b/vid-webpack-master/src/app/drawingBoard/service-planning/duplicate/duplicate.service.spec.ts index 3483885b5..50bfa936e 100644 --- a/vid-webpack-master/src/app/drawingBoard/service-planning/duplicate/duplicate.service.spec.ts +++ b/vid-webpack-master/src/app/drawingBoard/service-planning/duplicate/duplicate.service.spec.ts @@ -3,9 +3,10 @@ import {LogService} from '../../../shared/utils/log/log.service'; import {NgRedux} from '@angular-redux/store'; import {ITreeNode} from "angular-tree-component/dist/defs/api"; import {SdcUiServices} from "onap-ui-angular"; -import {IModalConfig} from "onap-ui-angular/dist/components/common"; +import {IModalConfig} from 'onap-ui-angular/dist/components/common'; import {AppState} from "../../../shared/store/reducers"; import {getTestBed, TestBed} from "@angular/core/testing"; +import {FeatureFlagsService} from "../../../shared/services/featureFlag/feature-flags.service"; class MockAppStore<T> { getState(){ @@ -52,15 +53,23 @@ class MockAppStore<T> { class MockModalService<T> {} +class MockFeatureFlagsService extends FeatureFlagsService{ + getAllFlags(): { [p: string]: boolean } { + return {}; + } +} + describe('Drawing board tree service', () => { let injector; let service: DuplicateService; let store : NgRedux<AppState>; + let featureFlagsService : FeatureFlagsService; beforeAll(done => (async () => { TestBed.configureTestingModule({ providers : [ DuplicateService, LogService, + {provide: FeatureFlagsService, useClass: MockFeatureFlagsService}, {provide: NgRedux, useClass: MockAppStore}, {provide: SdcUiServices.ModalService, useClass: MockModalService} ] @@ -70,6 +79,7 @@ describe('Drawing board tree service', () => { injector = getTestBed(); service = injector.get(DuplicateService); store = injector.get(NgRedux); + featureFlagsService = injector.get(FeatureFlagsService); })().then(done).catch(done.fail)); @@ -418,6 +428,11 @@ describe('Drawing board tree service', () => { } } + }, + global : { + flags : { + + } } } } diff --git a/vid-webpack-master/src/app/drawingBoard/service-planning/duplicate/duplicate.service.ts b/vid-webpack-master/src/app/drawingBoard/service-planning/duplicate/duplicate.service.ts index 5fb5f0b15..847790de9 100644 --- a/vid-webpack-master/src/app/drawingBoard/service-planning/duplicate/duplicate.service.ts +++ b/vid-webpack-master/src/app/drawingBoard/service-planning/duplicate/duplicate.service.ts @@ -11,15 +11,18 @@ import {TypeNodeInformation} from "../typeNodeInformation.model"; import {SdcUiCommon, SdcUiServices} from "onap-ui-angular"; import {changeInstanceCounter, duplicateBulkInstances} from "../../../shared/storeUtil/utils/general/general.actions"; import {IModalConfig} from "onap-ui-angular/dist/modals/models/modal-config"; +import {FeatureFlagsService} from "../../../shared/services/featureFlag/feature-flags.service"; +import {Utils} from "../../../shared/utils/utils"; @Injectable() export class DuplicateService { - constructor(private _logService : LogService, private _store: NgRedux<AppState>, modalService: SdcUiServices.ModalService) { + constructor(private _logService: LogService, private _store: NgRedux<AppState>, modalService: SdcUiServices.ModalService) { this.modalService = modalService; } - numberOfDuplicates:number; + numberOfDuplicates: number; + setNumberOfDuplicates(numberOfDuplicates: number) { this.numberOfDuplicates = numberOfDuplicates; } @@ -30,10 +33,9 @@ export class DuplicateService { storeKey: string = null; padding = '0000'; modalService: SdcUiServices.ModalService; - store : NgRedux<AppState>; - existingNames : {[key: string] : any}; - currentNode : ITreeNode = null; - + store: NgRedux<AppState>; + existingNames: { [key: string]: any }; + currentNode: ITreeNode = null; canDuplicate(node: ITreeNode): boolean { @@ -41,25 +43,31 @@ export class DuplicateService { return node.data.type === 'VF' || node.data.type === 'VL'; } - isEnabled(node: ITreeNode, store: NgRedux<AppState>, serviceId : string): boolean { - if(!_.isNil(node) && !_.isNil(node.data.menuActions['duplicate'])){ - if(this.hasMissingData(node)) return false; - const typeNodeInformation : TypeNodeInformation = new TypeNodeInformation(node); - const max : number = store.getState().service.serviceHierarchy[serviceId][typeNodeInformation.hierarchyName][node.data.modelName].properties['max_instances'] || 1; + isEnabled(node: ITreeNode, store: NgRedux<AppState>, serviceId: string): boolean { + if (!_.isNil(node) && !_.isNil(node.data.menuActions['duplicate'])) { + if (this.hasMissingData(node)) return false; + const typeNodeInformation: TypeNodeInformation = new TypeNodeInformation(node); + const flags = FeatureFlagsService.getAllFlags(store); + const currentExisting: number = store.getState().service.serviceInstance[serviceId][typeNodeInformation.existingMappingCounterName][node.data.modelUniqueId]; + const maxInstances = Utils.getMaxFirstLevel(store.getState().service.serviceHierarchy[serviceId][typeNodeInformation.hierarchyName][node.data.modelName].properties, flags); + if (_.isNil(maxInstances)) { + return true; + } else { + return maxInstances - currentExisting > 0; + } - return max - currentExisting > 0; - }else { + } else { return false; } } - hasMissingData(node : ITreeNode): boolean { - if(!_.isNil(node)){ - if(node.data.missingData) return true; - if(!_.isNil(node.data.children)){ - for(let child of node.data.children) { - if(child.missingData){ + hasMissingData(node: ITreeNode): boolean { + if (!_.isNil(node)) { + if (node.data.missingData) return true; + if (!_.isNil(node.data.children)) { + for (let child of node.data.children) { + if (child.missingData) { return true; } } @@ -69,16 +77,22 @@ export class DuplicateService { return false; } - getRemainsInstance(modelId : string, modelName : string, serviceId : string, store: NgRedux<AppState>, node : ITreeNode) : number { - const typeNodeInformation : TypeNodeInformation = new TypeNodeInformation(node); - const properties = store.getState().service.serviceHierarchy[serviceId][typeNodeInformation.hierarchyName][modelName].properties; - const currentExisting : number = store.getState().service.serviceInstance[serviceId][typeNodeInformation.existingMappingCounterName][modelId]; - return (!_.isNil(properties) && !_.isNil(properties['max_instances'])) ? properties['max_instances'] - currentExisting : null; + getRemainsInstance(modelId: string, modelName: string, serviceId: string, store: NgRedux<AppState>, node: ITreeNode): number { + const typeNodeInformation: TypeNodeInformation = new TypeNodeInformation(node); + const properties = store.getState().service.serviceHierarchy[serviceId][typeNodeInformation.hierarchyName][modelName].properties; + const currentExisting: number = store.getState().service.serviceInstance[serviceId][typeNodeInformation.existingMappingCounterName][modelId]; + + const flags = FeatureFlagsService.getAllFlags(store); + const maxInstances = Utils.getMaxFirstLevel(properties, flags); + if (_.isNil(maxInstances)) { + return 10; + } else { + return maxInstances - currentExisting; + } } - - openDuplicateModal(currentServiceId: string, currentUuid: string, currentId: string, storeKey : string, numberOfDuplicate: number, _store : NgRedux<AppState>, node: ITreeNode): IModalConfig { + openDuplicateModal(currentServiceId: string, currentUuid: string, currentId: string, storeKey: string, numberOfDuplicate: number, _store: NgRedux<AppState>, node: ITreeNode): IModalConfig { this.currentInstanceId = currentId; this.currentServiceId = currentServiceId; this.maxNumberOfDuplicate = this.getRemainsInstance(currentUuid, currentId, currentServiceId, _store, node); @@ -87,7 +101,7 @@ export class DuplicateService { this.currentNode = node; - return { + return { size: SdcUiCommon.ModalSize.medium, title: 'Duplicate Node', type: SdcUiCommon.ModalType.custom, @@ -98,12 +112,12 @@ export class DuplicateService { }; } - duplicate(node : ITreeNode): void { - const typeNodeInformation : TypeNodeInformation = new TypeNodeInformation(node); + duplicate(node: ITreeNode): void { + const typeNodeInformation: TypeNodeInformation = new TypeNodeInformation(node); this.existingNames = this.store.getState().service.serviceInstance[this.currentServiceId].existingNames; - const toClone = this.store.getState().service.serviceInstance[this.currentServiceId][typeNodeInformation.hierarchyName][this.storeKey]; + const toClone = this.store.getState().service.serviceInstance[this.currentServiceId][typeNodeInformation.hierarchyName][this.storeKey]; let newObjects = {}; - for(let i = 0; i < this.numberOfDuplicates; i++) { + for (let i = 0; i < this.numberOfDuplicates; i++) { const uniqueStoreKey = this.generateUniqueStoreKey(this.currentServiceId, this.currentInstanceId, this.store.getState().service.serviceInstance[this.currentServiceId][typeNodeInformation.hierarchyName], newObjects); const clone = this.cloneVnf(toClone, this.currentInstanceId); newObjects[uniqueStoreKey] = clone; @@ -114,12 +128,12 @@ export class DuplicateService { } - cloneVnf(vnf : VnfInstance, originalName: string): VnfInstance { - let newUniqueVnf : VnfInstance = _.cloneDeep(vnf); + cloneVnf(vnf: VnfInstance, originalName: string): VnfInstance { + let newUniqueVnf: VnfInstance = _.cloneDeep(vnf); newUniqueVnf.originalName = originalName; newUniqueVnf.trackById = DefaultDataGeneratorService.createRandomTrackById(); - if (!_.isNil(vnf.instanceName)){ + if (!_.isNil(vnf.instanceName)) { newUniqueVnf.instanceName = this.ensureUniqueNameOrGenerateOne(vnf.instanceName); } @@ -127,10 +141,10 @@ export class DuplicateService { const vfModuleModel: VfModuleMap = vnf.vfModules[vf_module_model_name]; for (let vfModule in vfModuleModel) { newUniqueVnf.vfModules[vf_module_model_name][vfModule].trackById = DefaultDataGeneratorService.createRandomTrackById(); - if (!_.isNil(vfModuleModel[vfModule].instanceName)){ + if (!_.isNil(vfModuleModel[vfModule].instanceName)) { newUniqueVnf.vfModules[vf_module_model_name][vfModule].instanceName = this.ensureUniqueNameOrGenerateOne(vfModuleModel[vfModule].instanceName); } - if (!_.isNil(vfModuleModel[vfModule].volumeGroupName)){ + if (!_.isNil(vfModuleModel[vfModule].volumeGroupName)) { newUniqueVnf.vfModules[vf_module_model_name][vfModule].volumeGroupName = this.ensureUniqueNameOrGenerateOne(vfModuleModel[vfModule].volumeGroupName); } } @@ -138,7 +152,7 @@ export class DuplicateService { return newUniqueVnf; } - ensureUniqueNameOrGenerateOne(instanceName){ + ensureUniqueNameOrGenerateOne(instanceName) { let uniqueInstanceName = instanceName; if (this.isAlreadyExists(instanceName, this.existingNames)) { uniqueInstanceName = this.generateNextUniqueName(instanceName, this.existingNames); @@ -148,33 +162,33 @@ export class DuplicateService { } - isAlreadyExists(name : string, existingNames : {[key: string] : any}){ + isAlreadyExists(name: string, existingNames: { [key: string]: any }) { return _.has(existingNames, name.toLowerCase()); } - generateNextUniqueName(name : string, existingNames : {[key: string] : any}) :string{ + generateNextUniqueName(name: string, existingNames: { [key: string]: any }): string { let suffix = "000"; let counter = 1; - if (name.match(/^.*_[\d]{3}$/)){ + if (name.match(/^.*_[\d]{3}$/)) { name = name.substring(0, name.length - 4); } - while(true){ - let paddingNumber : string = this.getNumberAsPaddingString(counter, suffix); + while (true) { + let paddingNumber: string = this.getNumberAsPaddingString(counter, suffix); let candidateUniqueName = name + '_' + paddingNumber; - if(!this.isAlreadyExists(candidateUniqueName, existingNames)){ + if (!this.isAlreadyExists(candidateUniqueName, existingNames)) { return candidateUniqueName; } counter++; } } - generateUniqueStoreKey(serviceId : string, objectName : string, existing : any, newObjects: any) : string { + generateUniqueStoreKey(serviceId: string, objectName: string, existing: any, newObjects: any): string { let counter = 1; - while(true){ - let paddingNumber : string = this.getNumberAsPaddingString(counter, this.padding); + while (true) { + let paddingNumber: string = this.getNumberAsPaddingString(counter, this.padding); const name = objectName + ':' + paddingNumber; - if(_.isNil(existing[name]) && _.isNil(newObjects[name])){ + if (_.isNil(existing[name]) && _.isNil(newObjects[name])) { return name; } counter++; diff --git a/vid-webpack-master/src/app/drawingBoard/service-planning/objectsToTree/models/network/network.model.info.spec.ts b/vid-webpack-master/src/app/drawingBoard/service-planning/objectsToTree/models/network/network.model.info.spec.ts index 10c13661f..089f812a7 100644 --- a/vid-webpack-master/src/app/drawingBoard/service-planning/objectsToTree/models/network/network.model.info.spec.ts +++ b/vid-webpack-master/src/app/drawingBoard/service-planning/objectsToTree/models/network/network.model.info.spec.ts @@ -12,6 +12,7 @@ import {DrawingBoardModes} from "../../../drawing-board.modes"; import {IframeService} from "../../../../../shared/utils/iframe.service"; import {DuplicateService} from "../../../duplicate/duplicate.service"; import {ModelInformationItem} from "../../../../../shared/components/model-information/model-information.component"; +import {FeatureFlagsService} from "../../../../../shared/services/featureFlag/feature-flags.service"; class MockAppStore<T> { getState() { @@ -19,27 +20,27 @@ class MockAppStore<T> { global: { 'drawingBoardStatus': DrawingBoardModes.CREATE }, - service : { - serviceHierarchy : { - 'servicedId' : { - 'networks' : { - 'networkName' : { - 'properties' : { - 'max_instances' : 1 + service: { + serviceHierarchy: { + 'servicedId': { + 'networks': { + 'networkName': { + 'properties': { + 'max_instances': 1 } } } } }, - serviceInstance : { - 'servicedId' : { - 'existingNetworksCounterMap' : { - 'networkId' : 1 + serviceInstance: { + 'servicedId': { + 'existingNetworksCounterMap': { + 'networkId': 1 }, - 'networks' : { - 'networkName' :{ + 'networks': { + 'networkName': { 'action': 'Create', - 'originalName' : 'networkName' + 'originalName': 'networkName' } } } @@ -48,15 +49,24 @@ class MockAppStore<T> { } } } + +class MockFeatureFlagsService extends FeatureFlagsService { + getAllFlags(): { [p: string]: boolean } { + return {}; + } +} + + describe('Network Model Info', () => { let injector; - let _dynamicInputsService : DynamicInputsService; - let _sharedTreeService : SharedTreeService; + let _dynamicInputsService: DynamicInputsService; + let _sharedTreeService: SharedTreeService; let networkModel: NetworkModelInfo; - let _dialogService : DialogService; - let _networkPopupService : NetworkPopupService; - let _duplicateService : DuplicateService; - let _iframeService : IframeService; + let _dialogService: DialogService; + let _networkPopupService: NetworkPopupService; + let _duplicateService: DuplicateService; + let _iframeService: IframeService; + let _featureFlagsService: FeatureFlagsService; beforeAll(done => (async () => { TestBed.configureTestingModule({ @@ -69,13 +79,16 @@ describe('Network Model Info', () => { IframeService, DuplicateService, {provide: NgRedux, useClass: MockAppStore}, + {provide: FeatureFlagsService, useClass: MockFeatureFlagsService}, MockNgRedux] }); await TestBed.compileComponents(); injector = getTestBed(); _sharedTreeService = injector.get(SharedTreeService); - networkModel = new NetworkModelInfo(_dynamicInputsService, _sharedTreeService, _dialogService, _networkPopupService, _duplicateService, null, _iframeService, MockNgRedux.getInstance()); + _featureFlagsService = injector.get(FeatureFlagsService); + + networkModel = new NetworkModelInfo(_dynamicInputsService, _sharedTreeService, _dialogService, _networkPopupService, _duplicateService, null, _iframeService, _featureFlagsService, MockNgRedux.getInstance()); })().then(done).catch(done.fail)); test('NetworkModelInfo should be defined', () => { @@ -137,39 +150,37 @@ describe('Network Model Info', () => { expect(model.type).toEqual('VL'); }); - test('showNodeIcons should return false if reachLimit of max', ()=>{ - let serviceId : string = 'servicedId'; + test('showNodeIcons should return false if reachLimit of max', () => { + let serviceId: string = 'servicedId'; let node = { - data : { - id : 'networkId', - name : 'networkName', - modelCustomizationId : 'modelCustomizationId' + data: { + id: 'networkId', + name: 'networkName', + modelCustomizationId: 'modelCustomizationId' } }; jest.spyOn(_sharedTreeService, 'getExistingInstancesWithDeleteMode').mockReturnValue(0); jest.spyOn(MockNgRedux.getInstance(), 'getState').mockReturnValue({ - global : {}, - service : { - serviceHierarchy : { - 'servicedId' : { - 'networks' : { - 'networkName' : { - 'properties' : { - 'max_instances' : 1 + global: {}, + service: { + serviceHierarchy: { + 'servicedId': { + 'networks': { + 'networkName': { + 'properties': { + 'max_instances': 1 } } } } }, - serviceInstance : { - 'servicedId' : { - 'existingNetworksCounterMap' : { - 'modelCustomizationId' : 1 + serviceInstance: { + 'servicedId': { + 'existingNetworksCounterMap': { + 'modelCustomizationId': 1 }, - 'networks' : { - 'networkName' :{ - - } + 'networks': { + 'networkName': {} } } } @@ -177,41 +188,39 @@ describe('Network Model Info', () => { }); let result = networkModel.showNodeIcons(<any>node, serviceId); - expect(result).toEqual(new AvailableNodeIcons(true , false)); + expect(result).toEqual(new AvailableNodeIcons(true, false)); }); - test('showNodeIcons should return true if not reachLimit of max', ()=>{ - let serviceId : string = 'servicedId'; + test('showNodeIcons should return true if not reachLimit of max', () => { + let serviceId: string = 'servicedId'; let node = { - data : { - id : 'networkId', - name : 'networkName' + data: { + id: 'networkId', + name: 'networkName' } }; jest.spyOn(_sharedTreeService, 'getExistingInstancesWithDeleteMode').mockReturnValue(0); jest.spyOn(MockNgRedux.getInstance(), 'getState').mockReturnValue({ - global : {}, - service : { - serviceHierarchy : { - 'servicedId' : { - 'networks' : { - 'networkName' : { - 'properties' : { - 'max_instances' : 2 + global: {}, + service: { + serviceHierarchy: { + 'servicedId': { + 'networks': { + 'networkName': { + 'properties': { + 'max_instances': 2 } } } } }, - serviceInstance : { - 'servicedId' : { - 'existingNetworksCounterMap' : { - 'networkId' : 1 + serviceInstance: { + 'servicedId': { + 'existingNetworksCounterMap': { + 'networkId': 1 }, - 'networks' : { - 'networkName' :{ - - } + 'networks': { + 'networkName': {} } } } @@ -219,34 +228,34 @@ describe('Network Model Info', () => { }); let result = networkModel.showNodeIcons(<any>node, serviceId); - expect(result).toEqual(new AvailableNodeIcons(true , false)); + expect(result).toEqual(new AvailableNodeIcons(true, false)); }); - test('getNodeCount should return number of nodes', ()=>{ - let serviceId : string = 'servicedId'; + test('getNodeCount should return number of nodes', () => { + let serviceId: string = 'servicedId'; jest.spyOn(MockNgRedux.getInstance(), 'getState').mockReturnValue({ - global : {}, - service : { - serviceHierarchy : { - 'servicedId' : { - 'networks' : { - 'networkName' : { - 'properties' : { - 'max_instances' : 1 + global: {}, + service: { + serviceHierarchy: { + 'servicedId': { + 'networks': { + 'networkName': { + 'properties': { + 'max_instances': 1 } } } } }, - serviceInstance : { - 'servicedId' : { - 'existingNetworksCounterMap' : { - 'modelCustomizationId' : 1 + serviceInstance: { + 'servicedId': { + 'existingNetworksCounterMap': { + 'modelCustomizationId': 1 }, - 'networks' : { - 'networkName' :{ + 'networks': { + 'networkName': { 'action': 'Create', - 'originalName' : 'networkName' + 'originalName': 'networkName' } } } @@ -255,24 +264,24 @@ describe('Network Model Info', () => { }); let node = { - data : { - id : 'networkId', - name : 'networkName', + data: { + id: 'networkId', + name: 'networkName', action: 'Create', - modelCustomizationId : "modelCustomizationId", + modelCustomizationId: "modelCustomizationId", modelUniqueId: "modelCustomizationId" } }; - let result = networkModel.getNodeCount(<any>node , serviceId); + let result = networkModel.getNodeCount(<any>node, serviceId); expect(result).toEqual(1); node.data.modelCustomizationId = 'networkId_notExist'; node.data.modelUniqueId = 'networkId_notExist'; - result = networkModel.getNodeCount(<any>node , serviceId); + result = networkModel.getNodeCount(<any>node, serviceId); expect(result).toEqual(0); }); - test('getMenuAction: showAuditInfoNetwork', ()=>{ + test('getMenuAction: showAuditInfoNetwork', () => { jest.spyOn(MockNgRedux.getInstance(), 'getState').mockReturnValue({ global: { @@ -281,7 +290,7 @@ describe('Network Model Info', () => { }); jest.spyOn(_sharedTreeService, 'isRetryMode').mockReturnValue(true); let node = { - data : { + data: { "modelId": "6b528779-44a3-4472-bdff-9cd15ec93450", "action": "Create", "isFailed": true, @@ -300,7 +309,7 @@ describe('Network Model Info', () => { test('Info for network should be correct', () => { const model = getNetworkModel(); const instance = getNetworkInstance(); - let actualNetworkInfo = networkModel.getInfo(model,instance); + let actualNetworkInfo = networkModel.getInfo(model, instance); let expectedNetworkInfo = [ ModelInformationItem.createInstance('Network role', "network role 1, network role 2"), ModelInformationItem.createInstance("Route target id", null), @@ -309,26 +318,27 @@ describe('Network Model Info', () => { expect(actualNetworkInfo).toEqual(expectedNetworkInfo); }); - function getNetworkModel(){ + function getNetworkModel() { return { - "customizationUuid":"94fdd893-4a36-4d70-b16a-ec29c54c184f", - "name":"ExtVL", - "version":"37.0", - "description":"ECOMP generic virtual link (network) base type for all other service-level and global networks", - "uuid":"ddc3f20c-08b5-40fd-af72-c6d14636b986", - "invariantUuid":"379f816b-a7aa-422f-be30-17114ff50b7c", - "max":1, - "min":0, - "isEcompGeneratedNaming":false, - "type":"VL", - "modelCustomizationName":"ExtVL 0", - "roles":["network role 1"," network role 2"], - "properties":{ - "network_role":"network role 1, network role 2", + "customizationUuid": "94fdd893-4a36-4d70-b16a-ec29c54c184f", + "name": "ExtVL", + "version": "37.0", + "description": "ECOMP generic virtual link (network) base type for all other service-level and global networks", + "uuid": "ddc3f20c-08b5-40fd-af72-c6d14636b986", + "invariantUuid": "379f816b-a7aa-422f-be30-17114ff50b7c", + "max": 1, + "min": 0, + "isEcompGeneratedNaming": false, + "type": "VL", + "modelCustomizationName": "ExtVL 0", + "roles": ["network role 1", " network role 2"], + "properties": { + "network_role": "network role 1, network role 2", "network_assignments": "{is_external_network=false, ipv4_subnet_default_assignment={min_subnets_count=1}, ecomp_generated_network_assignment=false, ipv6_subnet_default_assignment={min_subnets_count=1}}", - "exVL_naming":"{ecomp_generated_naming=true}","network_flows":"{is_network_policy=false, is_bound_to_vpn=false}", - "network_homing":"{ecomp_selected_instance_node_target=false}" + "exVL_naming": "{ecomp_generated_naming=true}", + "network_flows": "{is_network_policy=false, is_bound_to_vpn=false}", + "network_homing": "{ecomp_selected_instance_node_target=false}" } }; @@ -370,9 +380,7 @@ describe('Network Model Info', () => { } - - - function getServiceHierarchy(){ + function getServiceHierarchy() { return { "service": { "uuid": "6b528779-44a3-4472-bdff-9cd15ec93450", diff --git a/vid-webpack-master/src/app/drawingBoard/service-planning/objectsToTree/models/network/network.model.info.ts b/vid-webpack-master/src/app/drawingBoard/service-planning/objectsToTree/models/network/network.model.info.ts index 3ed40cd5f..3ba4a2c4b 100644 --- a/vid-webpack-master/src/app/drawingBoard/service-planning/objectsToTree/models/network/network.model.info.ts +++ b/vid-webpack-master/src/app/drawingBoard/service-planning/objectsToTree/models/network/network.model.info.ts @@ -30,6 +30,7 @@ import { import {IModalConfig} from "onap-ui-angular/dist/modals/models/modal-config"; import {ComponentInfoType} from "../../../component-info/component-info-model"; import {ModelInformationItem} from "../../../../../shared/components/model-information/model-information.component"; +import {FeatureFlagsService} from "../../../../../shared/services/featureFlag/feature-flags.service"; export class NetworkModelInfo implements ILevelNodeInfo { constructor(private _dynamicInputsService: DynamicInputsService, @@ -39,6 +40,7 @@ export class NetworkModelInfo implements ILevelNodeInfo { private _duplicateService: DuplicateService, private modalService: SdcUiServices.ModalService, private _iframeService: IframeService, + private _featureFlagsService: FeatureFlagsService, private _store: NgRedux<AppState>) { } @@ -70,7 +72,7 @@ export class NetworkModelInfo implements ILevelNodeInfo { ************************************************************/ getModel = (networkModelId: string, instance: NetworkInstance, serviceHierarchy): NetworkModel => { const originalModelName = instance.originalName ? instance.originalName : networkModelId; - return new NetworkModel(serviceHierarchy[this.name][originalModelName]); + return new NetworkModel(serviceHierarchy[this.name][originalModelName], this._featureFlagsService.getAllFlags()); }; @@ -164,8 +166,8 @@ export class NetworkModelInfo implements ILevelNodeInfo { counter -= this._sharedTreeService.getExistingInstancesWithDeleteMode(node, serviceModelId, 'networks'); const properties = this._store.getState().service.serviceHierarchy[serviceModelId].networks[node.data.name].properties; - const maxInstances: number = !_.isNil(properties) ? (properties.max_instances || 1) : 1; - const isReachedLimit = !(maxInstances > counter); + const flags = FeatureFlagsService.getAllFlags(this._store); + const isReachedLimit: boolean = this._sharedTreeService.isReachedToMaxInstances(properties, counter, flags); const showAddIcon = this._sharedTreeService.shouldShowAddIcon() && !isReachedLimit; return new AvailableNodeIcons(showAddIcon, isReachedLimit) diff --git a/vid-webpack-master/src/app/drawingBoard/service-planning/objectsToTree/models/relatedVnfMember/relatedVnfMember.info.model.spec.ts b/vid-webpack-master/src/app/drawingBoard/service-planning/objectsToTree/models/relatedVnfMember/relatedVnfMember.info.model.spec.ts index 840f31dcf..a44c21bf0 100644 --- a/vid-webpack-master/src/app/drawingBoard/service-planning/objectsToTree/models/relatedVnfMember/relatedVnfMember.info.model.spec.ts +++ b/vid-webpack-master/src/app/drawingBoard/service-planning/objectsToTree/models/relatedVnfMember/relatedVnfMember.info.model.spec.ts @@ -13,6 +13,7 @@ import {VnfPopupService} from "../../../../../shared/components/genericFormPopup import {DuplicateService} from "../../../duplicate/duplicate.service"; import {IframeService} from "../../../../../shared/utils/iframe.service"; import {RelatedVnfMemberInfoModel} from "./relatedVnfMember.info.model"; +import {VfModuleUpgradePopupService} from "../../../../../shared/components/genericFormPopup/genericFormServices/vfModuleUpgrade/vfModule.upgrade.popuop.service"; class MockAppStore<T> { @@ -38,6 +39,7 @@ describe('Related Vnf member Model Info', () => { DynamicInputsService, DialogService, VfModulePopuopService, + VfModuleUpgradePopupService, VnfPopupService, DefaultDataGeneratorService, SharedTreeService, diff --git a/vid-webpack-master/src/app/drawingBoard/service-planning/objectsToTree/models/vfModule/vfModule.model.info.spec.ts b/vid-webpack-master/src/app/drawingBoard/service-planning/objectsToTree/models/vfModule/vfModule.model.info.spec.ts index b596d0b48..276c0aeb2 100644 --- a/vid-webpack-master/src/app/drawingBoard/service-planning/objectsToTree/models/vfModule/vfModule.model.info.spec.ts +++ b/vid-webpack-master/src/app/drawingBoard/service-planning/objectsToTree/models/vfModule/vfModule.model.info.spec.ts @@ -16,6 +16,9 @@ import {ModelInformationItem} from "../../../../../shared/components/model-infor 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 {VfModuleUpgradePopupService} from "../../../../../shared/components/genericFormPopup/genericFormServices/vfModuleUpgrade/vfModule.upgrade.popuop.service"; +import {instance, mock, when} from "ts-mockito"; +import each from "jest-each"; class MockAppStore<T> { getState() { @@ -27,6 +30,12 @@ class MockAppStore<T> { } } +class MockFeatureFlagsService extends FeatureFlagsService{ + getAllFlags(): { [p: string]: boolean } { + return {}; + } +} + describe('VFModule Model Info', () => { let injector; let _dynamicInputsService : DynamicInputsService; @@ -34,8 +43,12 @@ describe('VFModule Model Info', () => { let vfModuleModel: VFModuleModelInfo; let _dialogService : DialogService; let _vfModulePopupService : VfModulePopuopService; + let _vfModuleUpgradePopupService : VfModuleUpgradePopupService; let _iframeService : IframeService; let _componentInfoService : ComponentInfoService; + let mockFeatureFlagsService: FeatureFlagsService = mock(FeatureFlagsService); + let mockFeatureFlagsServiceInstance: FeatureFlagsService = instance(mockFeatureFlagsService); + beforeAll(done => (async () => { TestBed.configureTestingModule({ @@ -44,6 +57,7 @@ describe('VFModule Model Info', () => { DynamicInputsService, DialogService, VfModulePopuopService, + VfModuleUpgradePopupService, SharedTreeService, IframeService, {provide: NgRedux, useClass: MockAppStore}, @@ -51,7 +65,7 @@ describe('VFModule Model Info', () => { AaiService, HttpClient, HttpHandler, - FeatureFlagsService, + {provide: FeatureFlagsService, useValue: mockFeatureFlagsServiceInstance}, ComponentInfoService ] }); @@ -59,8 +73,9 @@ describe('VFModule Model Info', () => { injector = getTestBed(); _sharedTreeService = injector.get(SharedTreeService); - _componentInfoService = injector.get(ComponentInfoService) - vfModuleModel = new VFModuleModelInfo(_dynamicInputsService, _sharedTreeService, _dialogService, _vfModulePopupService, _iframeService, MockNgRedux.getInstance(),_componentInfoService); + _componentInfoService = injector.get(ComponentInfoService); + vfModuleModel = new VFModuleModelInfo(_dynamicInputsService, _sharedTreeService, _dialogService, _vfModulePopupService, _vfModuleUpgradePopupService, + _iframeService, mockFeatureFlagsServiceInstance, MockNgRedux.getInstance(),_componentInfoService); })().then(done).catch(done.fail)); @@ -468,6 +483,33 @@ describe('VFModule Model Info', () => { expect(actualVNFInfo).toEqual(expectedVNFInfo); }); + each([ + ["maxCountInstances 3, currentNodeCount 1, flag on",{maxCountInstances:3}, 1, {FLAG_2002_UNLIMITED_MAX: true}, false], + ["maxCountInstances 3, currentNodeCount 3, flag on",{maxCountInstances:3}, 3, {FLAG_2002_UNLIMITED_MAX: true}, true], + ["no maxCountInstances, currentNodeCount 0, flag off",{}, 0, {FLAG_2002_UNLIMITED_MAX: false}, false], + ["no maxCountInstances, currentNodeCount 1, flag off",{}, 1, {FLAG_2002_UNLIMITED_MAX: false}, true], + ["no maxCountInstances, currentNodeCount 1, no flags",{}, 1, null, true], + ["no maxCountInstances, currentNodeCount 0, flag on",{}, 0, {FLAG_2002_UNLIMITED_MAX: true}, false], + ["no maxCountInstances, currentNodeCount 1, flag on",{}, 1, {FLAG_2002_UNLIMITED_MAX: true}, false], + ["no maxCountInstances, currentNodeCount 1000, flag on",{}, 1000, {FLAG_2002_UNLIMITED_MAX: true}, false], + ]).test('isVFModuleReachedLimit: %s', (desc, properties, currentNodeCount, flags, expected) => { + + const node = { data: { + name : 'vfModuleName' + }}; + + const serviceHierarchy = { + servicedId :{ + vfModules : { + vfModuleName : { + properties + }}}}; + + when(mockFeatureFlagsService.getAllFlags()).thenReturn(flags); + + expect(vfModuleModel.isVFModuleReachedLimit(node, serviceHierarchy, 'servicedId', currentNodeCount)).toEqual(expected); + }); + function getVFModule(){ return { "uuid":"522159d5-d6e0-4c2a-aa44-5a542a12a830", diff --git a/vid-webpack-master/src/app/drawingBoard/service-planning/objectsToTree/models/vfModule/vfModule.model.info.ts b/vid-webpack-master/src/app/drawingBoard/service-planning/objectsToTree/models/vfModule/vfModule.model.info.ts index 47a6dcb50..1ce452793 100644 --- a/vid-webpack-master/src/app/drawingBoard/service-planning/objectsToTree/models/vfModule/vfModule.model.info.ts +++ b/vid-webpack-master/src/app/drawingBoard/service-planning/objectsToTree/models/vfModule/vfModule.model.info.ts @@ -8,10 +8,7 @@ import {VfModuleInstance} from "../../../../../shared/models/vfModuleInstance"; import {VfModule} from "../../../../../shared/models/vfModule"; import {NgRedux} from "@angular-redux/store"; import {ITreeNode} from "angular-tree-component/dist/defs/api"; -import { - GenericFormPopupComponent, - PopupType -} from "../../../../../shared/components/genericFormPopup/generic-form-popup.component"; +import {GenericFormPopupComponent, PopupType} from "../../../../../shared/components/genericFormPopup/generic-form-popup.component"; import {DialogService} from "ng2-bootstrap-modal"; import {VfModulePopuopService} from "../../../../../shared/components/genericFormPopup/genericFormServices/vfModule/vfModule.popuop.service"; import {AppState} from "../../../../../shared/store/reducers"; @@ -19,24 +16,22 @@ import {MessageBoxData} from "../../../../../shared/components/messageBox/messag import {MessageBoxService} from "../../../../../shared/components/messageBox/messageBox.service"; import {AvailableNodeIcons} from "../../../available-models-tree/available-models-tree.service"; import {IframeService} from "../../../../../shared/utils/iframe.service"; -import { - deleteActionVfModuleInstance, - removeVfModuleInstance, - undoDeleteVfModuleInstance, - undoUgradeVFModule, - updateVFModulePosition, - upgradeVFModule -} from "../../../../../shared/storeUtil/utils/vfModule/vfModule.actions"; +import {deleteActionVfModuleInstance, deleteVFModuleField, removeVfModuleInstance, undoDeleteVfModuleInstance, undoUgradeVFModule, updateVFModulePosition, upgradeVFModule} from "../../../../../shared/storeUtil/utils/vfModule/vfModule.actions"; import {ComponentInfoService} from "../../../component-info/component-info.service"; import {ComponentInfoType} from "../../../component-info/component-info-model"; import {ModelInformationItem} from "../../../../../shared/components/model-information/model-information.component"; +import {VfModuleUpgradePopupService} from "../../../../../shared/components/genericFormPopup/genericFormServices/vfModuleUpgrade/vfModule.upgrade.popuop.service"; +import {FeatureFlagsService, Features} from "../../../../../shared/services/featureFlag/feature-flags.service"; +import {Utils} from "../../../../../shared/utils/utils"; export class VFModuleModelInfo implements ILevelNodeInfo { constructor(private _dynamicInputsService: DynamicInputsService, private _sharedTreeService: SharedTreeService, private _dialogService: DialogService, private _vfModulePopupService: VfModulePopuopService, + private _vfModuleUpgradePopupService: VfModuleUpgradePopupService, private _iframeService: IframeService, + private _featureFlagsService: FeatureFlagsService, private _store: NgRedux<AppState>, private _componentInfoService: ComponentInfoService) { } @@ -93,7 +88,7 @@ export class VFModuleModelInfo implements ILevelNodeInfo { newVfModule.typeName = this.typeName; newVfModule.menuActions = this.getMenuAction(<any>newVfModule, currentModel.uuid); newVfModule.isFailed = _.isNil(instance.isFailed) ? false : instance.isFailed; - newVfModule.statusMessage = !_.isNil(instance.statusMessage) ? instance.statusMessage: ""; + newVfModule.statusMessage = !_.isNil(instance.statusMessage) ? instance.statusMessage : ""; newVfModule = this._sharedTreeService.addingStatusProperty(newVfModule); return newVfModule; @@ -247,7 +242,7 @@ export class VFModuleModelInfo implements ILevelNodeInfo { if (!_.isNil(this._store.getState().service.serviceInstance[serviceModelId].vnfs[selectedVNF]) && node.parent.data.name === this._store.getState().service.serviceInstance[serviceModelId].vnfs[selectedVNF].originalName) { const existingVFModules = this.getCountVFModuleOfSelectedVNF(node, selectedVNF, serviceModelId); const reachedLimit = this.isVFModuleReachedLimit(node, this._store.getState().service.serviceHierarchy, serviceModelId, existingVFModules); - const showAddIcon = this._sharedTreeService.shouldShowAddIcon()&& !reachedLimit; + const showAddIcon = this._sharedTreeService.shouldShowAddIcon() && !reachedLimit; return new AvailableNodeIcons(showAddIcon, reachedLimit); } return new AvailableNodeIcons(false, false); @@ -297,13 +292,16 @@ export class VFModuleModelInfo implements ILevelNodeInfo { } isVFModuleReachedLimit(node: any, serviceHierarchy: any, serviceModelId: string, currentNodeCount: number): boolean { - let maxNodes: number = 1; + const flags = this._featureFlagsService.getAllFlags(); let vnfModules = serviceHierarchy[serviceModelId].vfModules; - if (vnfModules[node.data.name]) { - maxNodes = vnfModules[node.data.name].properties.maxCountInstances || 1; + const maxInstances = vnfModules[node.data.name] + ? Utils.getMaxVfModule(vnfModules[node.data.name].properties, flags) + : null; + if (_.isNil(maxInstances)) { + return false; } - return !(maxNodes > currentNodeCount); + return !(maxInstances > currentNodeCount); } getMenuAction(node: ITreeNode, serviceModelId: string): { [methodName: string]: { method: Function, visible: Function, enable: Function } } { @@ -351,15 +349,15 @@ export class VFModuleModelInfo implements ILevelNodeInfo { }, undoDelete: { method: (node, serviceModelId) => { - this._store.dispatch(undoDeleteVfModuleInstance(node.data.dynamicModelName, node.parent.data.vnfStoreKey, serviceModelId)) + this._store.dispatch(undoDeleteVfModuleInstance(node.data.dynamicModelName, node.parent.data.vnfStoreKey, serviceModelId)); + this._store.dispatch(deleteVFModuleField(node.data.modelName, node.parent.data.vnfStoreKey, node.data.servicedId ,node.data.dynamicModelName, 'retainAssignments')); }, visible: (node) => this._sharedTreeService.shouldShowUndoDelete(node), enable: (node, serviceModelId) => this._sharedTreeService.shouldShowUndoDelete(node) && this._sharedTreeService.shouldShowDelete(node.parent) && !this._sharedTreeService.isServiceOnDeleteMode(serviceModelId) }, - upgrade : { - method : (node, serviceModelId) => { - this._sharedTreeService.upgradeBottomUp(node, serviceModelId); - this._store.dispatch(upgradeVFModule(node.data.modelName, node.parent.data.vnfStoreKey, serviceModelId, node.data.dynamicModelName)); + upgrade: { + method: (node, serviceModelId) => { + this.upgradeVFM(serviceModelId, node); }, visible: (node,serviceModelId) => { return this._sharedTreeService.shouldShowUpgrade(node, serviceModelId); @@ -371,7 +369,7 @@ export class VFModuleModelInfo implements ILevelNodeInfo { undoUpgrade: { method: (node, serviceModelId) => { this._sharedTreeService.undoUpgradeBottomUp(node, serviceModelId); - this._store.dispatch(undoUgradeVFModule(node.data.modelName, node.parent.data.vnfStoreKey, serviceModelId, node.data.dynamicModelName)); + this._store.dispatch(undoUgradeVFModule(node.data.modelName, node.parent.data.vnfStoreKey, serviceModelId, node.data.dynamicModelName)); }, visible: (node) => { return this._sharedTreeService.shouldShowUndoUpgrade(node); @@ -383,7 +381,31 @@ export class VFModuleModelInfo implements ILevelNodeInfo { }; } - updatePosition(that , node, instanceId, parentStoreKey): void { + private upgradeVFM(serviceModelId, node) { + if (FeatureFlagsService.getFlagState(Features.FLAG_2002_VFM_UPGRADE_ADDITIONAL_OPTIONS, this._store)) { + this._iframeService.addClassOpenModal('content'); + this._dialogService.addDialog(GenericFormPopupComponent, { + type: PopupType.VF_MODULE_UPGRADE, + uuidData: <any>{ + serviceId: serviceModelId, + modelName: node.data.modelName, + vFModuleStoreKey: node.data.dynamicModelName, + vnfStoreKey: node.parent.data.vnfStoreKey, + modelId: node.data.modelId, + type: node.data.type, + popupService: this._vfModuleUpgradePopupService, + vfModule : _.cloneDeep(node) + }, + node, + isUpdateMode: false + }); + }else { + this._sharedTreeService.upgradeBottomUp(node, serviceModelId); + this._store.dispatch(upgradeVFModule(node.data.modelName, node.parent.data.vnfStoreKey, serviceModelId ,node.data.dynamicModelName)); + } + } + + updatePosition(that, node, instanceId, parentStoreKey): void { that.store.dispatch(updateVFModulePosition(node, instanceId, parentStoreKey)); } @@ -395,12 +417,12 @@ export class VFModuleModelInfo implements ILevelNodeInfo { getInfo(model, instance): ModelInformationItem[] { const modelInformation = !_.isEmpty(model) && !_.isEmpty(model.properties) ? [ ModelInformationItem.createInstance("Base module", model.properties.baseModule), - ModelInformationItem.createInstance("Min instances", !_.isNull(model.properties.minCountInstances)? String(model.properties.minCountInstances): null), - ModelInformationItem.createInstance("Max instances", !_.isNull(model.properties.maxCountInstances)? String(model.properties.maxCountInstances): null), - ModelInformationItem.createInstance("Initial instances count", !_.isNull(model.properties.initialCount)? String(model.properties.initialCount): null) + ModelInformationItem.createInstance("Min instances", !_.isNull(model.properties.minCountInstances) ? String(model.properties.minCountInstances) : null), + ModelInformationItem.createInstance("Max instances", !_.isNull(model.properties.maxCountInstances) ? String(model.properties.maxCountInstances) : null), + ModelInformationItem.createInstance("Initial instances count", !_.isNull(model.properties.initialCount) ? String(model.properties.initialCount) : null) ] : []; - const instanceInfo = []; + const instanceInfo = []; const result = [modelInformation, instanceInfo]; return _.uniq(_.flatten(result)); } diff --git a/vid-webpack-master/src/app/drawingBoard/service-planning/objectsToTree/models/vnf/vnf.model.info.spec.ts b/vid-webpack-master/src/app/drawingBoard/service-planning/objectsToTree/models/vnf/vnf.model.info.spec.ts index de8962787..3ac6076dc 100644 --- a/vid-webpack-master/src/app/drawingBoard/service-planning/objectsToTree/models/vnf/vnf.model.info.spec.ts +++ b/vid-webpack-master/src/app/drawingBoard/service-planning/objectsToTree/models/vnf/vnf.model.info.spec.ts @@ -22,6 +22,13 @@ import {ComponentInfoService} from "../../../component-info/component-info.servi 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 {VfModuleUpgradePopupService} from "../../../../../shared/components/genericFormPopup/genericFormServices/vfModuleUpgrade/vfModule.upgrade.popuop.service"; + +class MockFeatureFlagsService extends FeatureFlagsService{ + getAllFlags(): { [p: string]: boolean } { + return {}; + } +} describe('Vnf Model Info', () => { let injector; @@ -32,10 +39,12 @@ describe('Vnf Model Info', () => { let _defaultDataGeneratorService : DefaultDataGeneratorService; let _dialogService : DialogService; let _vfModulePopupService : VfModulePopuopService; + let _vfModuleUpgradePopupService : VfModuleUpgradePopupService; let _vnfPopupService : VnfPopupService; let _duplicateService : DuplicateService; let _iframeService : IframeService; let _componentInfoService : ComponentInfoService; + let _featureFlagsService : FeatureFlagsService; let _store : NgRedux<AppState>; let vnfModel: VnfModelInfo; @@ -47,6 +56,7 @@ describe('Vnf Model Info', () => { DynamicInputsService, DialogService, VfModulePopuopService, + VfModuleUpgradePopupService, VnfPopupService, DefaultDataGeneratorService, SharedTreeService, @@ -54,7 +64,7 @@ describe('Vnf Model Info', () => { AaiService, HttpClient, HttpHandler, - FeatureFlagsService, + {provide: FeatureFlagsService, useClass: MockFeatureFlagsService}, ComponentInfoService, IframeService] }).compileComponents(); @@ -62,7 +72,7 @@ describe('Vnf Model Info', () => { injector = getTestBed(); _sharedTreeService = injector.get(SharedTreeService); _store = injector.get(NgRedux); - _componentInfoService = injector.get(ComponentInfoService); + _featureFlagsService = injector.get(FeatureFlagsService); vnfModel = new VnfModelInfo( _dynamicInputsService, @@ -71,10 +81,12 @@ describe('Vnf Model Info', () => { _dialogService, _vnfPopupService, _vfModulePopupService, + _vfModuleUpgradePopupService, _duplicateService, null, _iframeService, _componentInfoService, + _featureFlagsService, _store); @@ -456,6 +468,12 @@ describe('Vnf Model Info', () => { expect(actualVNFInfo).toEqual(expectedVNFInfo); }); + test('When there is no max Max instances text is: Unlimited (default)', () => { + let actualVNFInfo = vnfModel.getInfo({just:"not empty"},null); + const maxInstancesItem = actualVNFInfo.find((item)=> item.label == 'Max instances'); + expect(maxInstancesItem.values[0]).toEqual('Unlimited (default)'); + }); + function getVNFModel(){ return { "name":"VF_vGeraldine", diff --git a/vid-webpack-master/src/app/drawingBoard/service-planning/objectsToTree/models/vnf/vnf.model.info.ts b/vid-webpack-master/src/app/drawingBoard/service-planning/objectsToTree/models/vnf/vnf.model.info.ts index ebcba162e..ff86925f1 100644 --- a/vid-webpack-master/src/app/drawingBoard/service-planning/objectsToTree/models/vnf/vnf.model.info.ts +++ b/vid-webpack-master/src/app/drawingBoard/service-planning/objectsToTree/models/vnf/vnf.model.info.ts @@ -9,10 +9,7 @@ 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 { - GenericFormPopupComponent, - PopupType -} from "../../../../../shared/components/genericFormPopup/generic-form-popup.component"; +import {GenericFormPopupComponent, PopupType} from "../../../../../shared/components/genericFormPopup/generic-form-popup.component"; import {DialogService} from 'ng2-bootstrap-modal'; import {VnfPopupService} from "../../../../../shared/components/genericFormPopup/genericFormServices/vnf/vnf.popup.service"; import {VfModulePopuopService} from "../../../../../shared/components/genericFormPopup/genericFormServices/vfModule/vfModule.popuop.service"; @@ -26,18 +23,15 @@ import {changeInstanceCounter, removeInstance} from "../../../../../shared/store import {MessageBoxData} from "../../../../../shared/components/messageBox/messageBox.data"; import {MessageBoxService} from "../../../../../shared/components/messageBox/messageBox.service"; import {ServiceInstanceActions} from "../../../../../shared/models/serviceInstanceActions"; -import { - deleteActionVnfInstance, - undoDeleteActionVnfInstance, - undoUpgradeVnf, - updateVnfPosition, - upgradeVnf -} from "../../../../../shared/storeUtil/utils/vnf/vnf.actions"; +import {deleteActionVnfInstance, undoDeleteActionVnfInstance, undoUpgradeVnf, updateVnfPosition, upgradeVnf} from "../../../../../shared/storeUtil/utils/vnf/vnf.actions"; import * as _ from 'lodash'; import {IModalConfig} from "onap-ui-angular/dist/modals/models/modal-config"; import {ComponentInfoType} from "../../../component-info/component-info-model"; import {ComponentInfoService} from "../../../component-info/component-info.service"; import {ModelInformationItem} from "../../../../../shared/components/model-information/model-information.component"; +import {VfModuleUpgradePopupService} from "../../../../../shared/components/genericFormPopup/genericFormServices/vfModuleUpgrade/vfModule.upgrade.popuop.service"; +import {FeatureFlagsService} from "../../../../../shared/services/featureFlag/feature-flags.service"; +import {Constants} from "../../../../../shared/utils/constants"; export class VnfModelInfo implements ILevelNodeInfo { constructor(private _dynamicInputsService: DynamicInputsService, @@ -46,10 +40,12 @@ export class VnfModelInfo implements ILevelNodeInfo { private _dialogService: DialogService, private _vnfPopupService: VnfPopupService, private _vfModulePopupService: VfModulePopuopService, + private _vfModuleUpgradePopupService: VfModuleUpgradePopupService, private _duplicateService: DuplicateService, private modalService: SdcUiServices.ModalService, private _iframeService: IframeService, private _componentInfoService: ComponentInfoService, + private _featureFlagsService: FeatureFlagsService, private _store: NgRedux<AppState>) { } @@ -85,7 +81,7 @@ export class VnfModelInfo implements ILevelNodeInfo { ************************************************************/ getModel = (vnfModelId: string, instance: VnfInstance, serviceHierarchy): VNFModel => { const originalModelName = instance.originalName ? instance.originalName : vnfModelId; - return new VNFModel(serviceHierarchy[this.name][originalModelName]); + return new VNFModel(serviceHierarchy[this.name][originalModelName], this._featureFlagsService.getAllFlags()); }; @@ -102,7 +98,7 @@ export class VnfModelInfo implements ILevelNodeInfo { node.typeName = this.typeName; node.menuActions = this.getMenuAction(<any>node, model.uuid); node.isFailed = _.isNil(instance.isFailed) ? false : instance.isFailed; - node.statusMessage = !_.isNil(instance.statusMessage) ? instance.statusMessage: ""; + node.statusMessage = !_.isNil(instance.statusMessage) ? instance.statusMessage : ""; node = this._sharedTreeService.addingStatusProperty(node); return node; }; @@ -111,7 +107,7 @@ export class VnfModelInfo implements ILevelNodeInfo { * return next level object (VFModule) ************************************************************/ getNextLevelObject = (): VFModuleModelInfo => { - return new VFModuleModelInfo(this._dynamicInputsService, this._sharedTreeService, this._dialogService, this._vfModulePopupService, this._iframeService, this._store, this._componentInfoService); + return new VFModuleModelInfo(this._dynamicInputsService, this._sharedTreeService, this._dialogService, this._vfModulePopupService, this._vfModuleUpgradePopupService, this._iframeService, this._featureFlagsService, this._store, this._componentInfoService); }; /*********************************************************** @@ -179,8 +175,8 @@ export class VnfModelInfo implements ILevelNodeInfo { counter -= this._sharedTreeService.getExistingInstancesWithDeleteMode(node, serviceModelId, 'vnfs'); const properties = this._store.getState().service.serviceHierarchy[serviceModelId].vnfs[node.data.name].properties; - const maxInstances: number = !_.isNil(properties) ? (properties.max_instances || 1) : 1; - const isReachedLimit = !(maxInstances > counter); + const flags = FeatureFlagsService.getAllFlags(this._store); + const isReachedLimit: boolean = this._sharedTreeService.isReachedToMaxInstances(properties, counter, flags); const showAddIcon = this._sharedTreeService.shouldShowAddIcon() && !isReachedLimit; return new AvailableNodeIcons(showAddIcon, isReachedLimit) } @@ -209,9 +205,9 @@ export class VnfModelInfo implements ILevelNodeInfo { }, showAuditInfo: { method: (node, serviceModelId) => { - const instance = this._store.getState().service.serviceInstance[serviceModelId].vnfs[node.data.vnfStoreKey]; - this._sharedTreeService.openAuditInfoModal(node, serviceModelId, instance, 'VNF', this); - }, + const instance = this._store.getState().service.serviceInstance[serviceModelId].vnfs[node.data.vnfStoreKey]; + this._sharedTreeService.openAuditInfoModal(node, serviceModelId, instance, 'VNF', this); + }, visible: (node) => this._sharedTreeService.shouldShowAuditInfo(node), enable: (node) => this._sharedTreeService.shouldShowAuditInfo(node) }, @@ -322,8 +318,9 @@ export class VnfModelInfo implements ILevelNodeInfo { getInfo(model, instance): ModelInformationItem[] { const modelInformation = !_.isEmpty(model) ? [ - ModelInformationItem.createInstance("Min instances", !_.isNull(model.min)? String(model.min): null), - ModelInformationItem.createInstance("Max instances", !_.isNull(model.max)? String(model.max): null) + ModelInformationItem.createInstance("Min instances", !_.isNil(model.min) ? String(model.min) : null), + ModelInformationItem.createInstance("Max instances", !_.isNil(model.max) ? String(model.max) : + Constants.ModelInfo.UNLIMITED_DEFAULT) ] : []; const instanceInfo = !_.isEmpty(instance) ? [ diff --git a/vid-webpack-master/src/app/drawingBoard/service-planning/objectsToTree/models/vrf/vrf.model.info.ts b/vid-webpack-master/src/app/drawingBoard/service-planning/objectsToTree/models/vrf/vrf.model.info.ts index 4c779a313..3dbc60adb 100644 --- a/vid-webpack-master/src/app/drawingBoard/service-planning/objectsToTree/models/vrf/vrf.model.info.ts +++ b/vid-webpack-master/src/app/drawingBoard/service-planning/objectsToTree/models/vrf/vrf.model.info.ts @@ -26,12 +26,14 @@ import { undoDeleteActionVrfInstance } from "../../../../../shared/storeUtil/utils/vrf/vrf.actions"; import * as _ from "lodash"; +import {FeatureFlagsService} from "../../../../../shared/services/featureFlag/feature-flags.service"; export class VrfModelInfo implements ILevelNodeInfo { constructor(private _store: NgRedux<AppState>, private _sharedTreeService: SharedTreeService, private _dialogService: DialogService, private _iframeService: IframeService, + private _featureFlagsService : FeatureFlagsService, private _networkStepService: NetworkStepService, private _vpnStepService: VpnStepService) { } @@ -70,7 +72,7 @@ export class VrfModelInfo implements ILevelNodeInfo { return new VpnModelInfo(this._store, this._sharedTreeService); } else { if (nextLevelType === 'networks') { - return new NetworkModelInfo(null, this._sharedTreeService, null, null, null, null, null, this._store); + return new NetworkModelInfo(null, this._sharedTreeService, null, null, null, null, null,this._featureFlagsService, this._store); } } }; diff --git a/vid-webpack-master/src/app/drawingBoard/service-planning/objectsToTree/models/vrf/vrfModal/networkStep/network.step.service.spec.ts b/vid-webpack-master/src/app/drawingBoard/service-planning/objectsToTree/models/vrf/vrfModal/networkStep/network.step.service.spec.ts index c7c8d07a0..6ad640317 100644 --- a/vid-webpack-master/src/app/drawingBoard/service-planning/objectsToTree/models/vrf/vrfModal/networkStep/network.step.service.spec.ts +++ b/vid-webpack-master/src/app/drawingBoard/service-planning/objectsToTree/models/vrf/vrfModal/networkStep/network.step.service.spec.ts @@ -84,7 +84,6 @@ describe('Network step service', () => { "genericModalCriteria": {"roles": ["-- select an option --", "network role 1", "network role 2", "network role 3", "network role 4", "network role 5"]}, "name": null, "flags": { - "CREATE_INSTANCE_TEST": false, "EMPTY_DRAWING_BOARD_TEST": false, "FLAG_NETWORK_TO_ASYNC_INSTANTIATION": false, "FLAG_ENABLE_WEBPACK_MODERN_UI": true, @@ -95,12 +94,9 @@ describe('Network step service', () => { "FLAG_SHOW_ASSIGNMENTS": true, "FLAG_FABRIC_CONFIGURATION_ASSIGNMENTS": true, "FLAG_DUPLICATE_VNF": true, - "FLAG_DEFAULT_VNF": true, - "FLAG_SETTING_DEFAULTS_IN_DRAWING_BOARD": true, "FLAG_A_LA_CARTE_AUDIT_INFO": true, "FLAG_1810_CR_ADD_CLOUD_OWNER_TO_MSO_REQUEST": true, "FLAG_PRESENT_PROVIDER_NETWORKS_ASSOCIATIONS": true, - "FLAG_1810_CR_SOFT_DELETE_ALACARTE_VF_MODULE": true, "FLAG_1902_NEW_VIEW_EDIT": true, "FLAG_VF_MODULE_RESUME_STATUS_CREATE": true, "FLAG_1906_COMPONENT_INFO": true diff --git a/vid-webpack-master/src/app/drawingBoard/service-planning/objectsToTree/objectToInstanceTree/objectToInstanceTree.service.spec.ts b/vid-webpack-master/src/app/drawingBoard/service-planning/objectsToTree/objectToInstanceTree/objectToInstanceTree.service.spec.ts index 7ab2f5b5b..9add349db 100644 --- a/vid-webpack-master/src/app/drawingBoard/service-planning/objectsToTree/objectToInstanceTree/objectToInstanceTree.service.spec.ts +++ b/vid-webpack-master/src/app/drawingBoard/service-planning/objectsToTree/objectToInstanceTree/objectToInstanceTree.service.spec.ts @@ -32,6 +32,7 @@ import {ErrorMsgService} from "../../../../shared/components/error-msg/error-msg import {ComponentInfoService} from "../../component-info/component-info.service"; import {NetworkStepService} from "../models/vrf/vrfModal/networkStep/network.step.service"; import {VpnStepService} from "../models/vrf/vrfModal/vpnStep/vpn.step.service"; +import {VfModuleUpgradePopupService} from "../../../../shared/components/genericFormPopup/genericFormServices/vfModuleUpgrade/vfModule.upgrade.popuop.service"; class MockAppStore<T> { getState() { @@ -73,6 +74,7 @@ describe('Model Tree Generator service', () => { NetworkPopupService, NetworkControlGenerator, VfModulePopuopService, + VfModuleUpgradePopupService, VfModuleControlGenerator, VnfGroupControlGenerator, DialogService, diff --git a/vid-webpack-master/src/app/drawingBoard/service-planning/objectsToTree/objectToModelTree/objectToModelTree.service.spec.ts b/vid-webpack-master/src/app/drawingBoard/service-planning/objectsToTree/objectToModelTree/objectToModelTree.service.spec.ts index 7246adc27..e5559ee15 100644 --- a/vid-webpack-master/src/app/drawingBoard/service-planning/objectsToTree/objectToModelTree/objectToModelTree.service.spec.ts +++ b/vid-webpack-master/src/app/drawingBoard/service-planning/objectsToTree/objectToModelTree/objectToModelTree.service.spec.ts @@ -35,6 +35,7 @@ import {ComponentInfoService} from "../../component-info/component-info.service" import {IModelTreeNodeModel} from "../../../objectsToTree/objectToModelTree/modelTreeNode.model"; import {VpnStepService} from "../models/vrf/vrfModal/vpnStep/vpn.step.service"; import {NetworkStepService} from "../models/vrf/vrfModal/networkStep/network.step.service"; +import {VfModuleUpgradePopupService} from "../../../../shared/components/genericFormPopup/genericFormServices/vfModuleUpgrade/vfModule.upgrade.popuop.service"; class MockAppStore<T> { getState() { @@ -98,6 +99,7 @@ describe('Model Tree Generator service', () => { NetworkPopupService, NetworkControlGenerator, VfModulePopuopService, + VfModuleUpgradePopupService, VfModuleControlGenerator, VnfGroupControlGenerator, DialogService, diff --git a/vid-webpack-master/src/app/drawingBoard/service-planning/objectsToTree/objectToModelTree/objectToModelTree.service.ts b/vid-webpack-master/src/app/drawingBoard/service-planning/objectsToTree/objectToModelTree/objectToModelTree.service.ts index c101f44e9..75ae1e149 100644 --- a/vid-webpack-master/src/app/drawingBoard/service-planning/objectsToTree/objectToModelTree/objectToModelTree.service.ts +++ b/vid-webpack-master/src/app/drawingBoard/service-planning/objectsToTree/objectToModelTree/objectToModelTree.service.ts @@ -3,11 +3,13 @@ import {ILevelNodeInfo} from "../models/basic.model.info"; import {ObjectToTreeService} from "../objectToTree.service"; import * as _ from "lodash"; import {IModelTreeNodeModel} from "../../../objectsToTree/objectToModelTree/modelTreeNode.model"; +import {FeatureFlagsService} from "../../../../shared/services/featureFlag/feature-flags.service"; @Injectable() export class ObjectToModelTreeService { numberOfPlusButton: number; - constructor(private _objectToTreeService: ObjectToTreeService) { + + constructor(private _objectToTreeService: ObjectToTreeService, private _featureFlagsService: FeatureFlagsService) { } /*********************************************************** @@ -18,9 +20,10 @@ export class ObjectToModelTreeService { let _this = this; const firstLevelOptions: ILevelNodeInfo[] = _this._objectToTreeService.getFirstLevelOptions(); let nodes = []; + let flags = this._featureFlagsService.getAllFlags(); for (let option of firstLevelOptions) { _.forOwn(serviceModel[option.name], function (item, key) { - nodes.push(_this.addFirstLevelModel(serviceModel.service.uuid, key, item, item.type, serviceModel, option)); + nodes.push(_this.addFirstLevelModel(serviceModel.service.uuid, key, item, item.type, serviceModel, option, flags)); }); } @@ -30,10 +33,10 @@ export class ObjectToModelTreeService { return nodes; } - calculateNumberOfNodesWithPlusIcon(serviceModel, nodes) : void { - this.numberOfPlusButton = nodes.reduce((sum, node)=>{ - let showNodeIconResult = node.showNodeIcons({data : node}, serviceModel.service.uuid); - return (!_.isNil(showNodeIconResult) && showNodeIconResult.addIcon && !showNodeIconResult.vIcon) ? sum + 1 : sum; + calculateNumberOfNodesWithPlusIcon(serviceModel, nodes): void { + this.numberOfPlusButton = nodes.reduce((sum, node) => { + let showNodeIconResult = node.showNodeIcons({data: node}, serviceModel.service.uuid); + return (!_.isNil(showNodeIconResult) && showNodeIconResult.addIcon && !showNodeIconResult.vIcon) ? sum + 1 : sum; }, 0); } @@ -47,9 +50,9 @@ export class ObjectToModelTreeService { * @param parentModel - current parent Model object * @param levelNodeInfo - current level node information ************************************************************/ - private addFirstLevelModel(serviceId: string, name, currentModel, type, parentModel, levelNodeInfo: ILevelNodeInfo) { - let node = ObjectToModelTreeService.convertItemToTreeNode(serviceId, name, currentModel, type, null, levelNodeInfo); - node.children = this.addNextLevelNodes(serviceId, currentModel, parentModel, levelNodeInfo, node); + private addFirstLevelModel(serviceId: string, name, currentModel, type, parentModel, levelNodeInfo: ILevelNodeInfo, flags?: { [key: string]: boolean }) { + let node = ObjectToModelTreeService.convertItemToTreeNode(serviceId, name, currentModel, type, null, levelNodeInfo, flags); + node.children = this.addNextLevelNodes(serviceId, currentModel, parentModel, levelNodeInfo, node, flags); return node; } @@ -61,13 +64,13 @@ export class ObjectToModelTreeService { * @param levelNodeInfo - current level node information * @param parentNode - parent node. ************************************************************/ - addNextLevelNodes(serviceId: string, currentModel, parentModel, levelNodeInfo: ILevelNodeInfo, parentNode): any[] { + addNextLevelNodes(serviceId: string, currentModel, parentModel, levelNodeInfo: ILevelNodeInfo, parentNode, flags?: { [key: string]: boolean }): any[] { if (!_.isNil(levelNodeInfo.childNames) && levelNodeInfo.childNames.length > 0) { levelNodeInfo.childNames.forEach(function (childName) { if (!_.isNil(currentModel[childName])) { let nextLevelNodeInfo = levelNodeInfo.getNextLevelObject.apply(this, [childName]); parentNode.children = Object.keys(currentModel[childName]).map((key) => - ObjectToModelTreeService.convertItemToTreeNode(serviceId, key, currentModel[childName][key], childName, currentModel, nextLevelNodeInfo)); + ObjectToModelTreeService.convertItemToTreeNode(serviceId, key, currentModel[childName][key], childName, currentModel, nextLevelNodeInfo, flags)); } }) } @@ -84,17 +87,18 @@ export class ObjectToModelTreeService { * @param parentModel - current parent model * @param levelNodeInfo - current levelNodeInfo object ************************************************************/ - static convertItemToTreeNode(serviceId: string, name: string, currentModel: any, valueType: string, parentModel: string, levelNodeInfo: ILevelNodeInfo) { - let node : IModelTreeNodeModel = { + static convertItemToTreeNode(serviceId: string, name: string, currentModel: any, valueType: string, parentModel: string, levelNodeInfo: ILevelNodeInfo, flags?: { [key: string]: boolean }) { + const type: string = levelNodeInfo.getType(); + let node: IModelTreeNodeModel = { id: currentModel.customizationUuid || currentModel.uuid, - modelCustomizationId : currentModel.customizationUuid, - modelVersionId: currentModel.uuid, - modelUniqueId : currentModel.customizationUuid || currentModel.uuid, + modelCustomizationId: currentModel.customizationUuid, + modelVersionId: currentModel.uuid, + modelUniqueId: currentModel.customizationUuid || currentModel.uuid, name: name, tooltip: levelNodeInfo.getTooltip(), - type: levelNodeInfo.getType(), + type, count: currentModel.count || 0, - max: currentModel.max || 1, + max: ObjectToModelTreeService.getMax(currentModel, type, flags), children: [], disabled: false, dynamicInputs: levelNodeInfo.updateDynamicInputsDataFromModel(currentModel), @@ -105,15 +109,24 @@ export class ObjectToModelTreeService { return node; } + static getMax(currentModel, type, flags: { [key: string]: boolean }) { + if (flags && !!flags['FLAG_2002_UNLIMITED_MAX'] && (type === 'VF' || type === 'Network' || type === 'VFmodule')) { + return !_.isNil(currentModel.max) ? currentModel.max : null; + } else { + return currentModel.max || 1 + } + } - static addExtraFunctionality(node, serviceId: string, name: string, currentModel: any, valueType: string, parentModel: string, levelNodeInfo: ILevelNodeInfo){ + + static addExtraFunctionality(node, serviceId: string, name: string, currentModel: any, valueType: string, parentModel: string, levelNodeInfo: ILevelNodeInfo) { node.onAddClick = (node, serviceId) => levelNodeInfo.onClickAdd(node, serviceId); node.getNodeCount = (node, serviceId) => levelNodeInfo.getNodeCount(node, serviceId); node.getMenuAction = (node, serviceId) => levelNodeInfo.getMenuAction(node, serviceId); node.showNodeIcons = (node, serviceId) => levelNodeInfo.showNodeIcons(node, serviceId); node.typeName = levelNodeInfo.typeName; node.getModel = levelNodeInfo.getModel.bind(levelNodeInfo); - node.getInfo = !_.isNil(levelNodeInfo.getInfo) ? levelNodeInfo.getInfo.bind(levelNodeInfo) : ()=>{}; + node.getInfo = !_.isNil(levelNodeInfo.getInfo) ? levelNodeInfo.getInfo.bind(levelNodeInfo) : () => { + }; node.componentInfoType = levelNodeInfo.componentInfoType; return node; } diff --git a/vid-webpack-master/src/app/drawingBoard/service-planning/objectsToTree/objectToTree.service.ts b/vid-webpack-master/src/app/drawingBoard/service-planning/objectsToTree/objectToTree.service.ts index 0072196f2..1e6825130 100644 --- a/vid-webpack-master/src/app/drawingBoard/service-planning/objectsToTree/objectToTree.service.ts +++ b/vid-webpack-master/src/app/drawingBoard/service-planning/objectsToTree/objectToTree.service.ts @@ -24,6 +24,8 @@ import {AaiService} from "../../../shared/services/aaiService/aai.service"; import {VrfModelInfo} from "./models/vrf/vrf.model.info"; import {NetworkStepService} from "./models/vrf/vrfModal/networkStep/network.step.service"; import {VpnStepService} from "./models/vrf/vrfModal/vpnStep/vpn.step.service"; +import { VfModuleUpgradePopupService } from "../../../shared/components/genericFormPopup/genericFormServices/vfModuleUpgrade/vfModule.upgrade.popuop.service"; +import {FeatureFlagsService} from "../../../shared/services/featureFlag/feature-flags.service"; @Injectable() export class ObjectToTreeService { @@ -34,6 +36,7 @@ export class ObjectToTreeService { private _vnfPopupService : VnfPopupService, private _networkPopupService : NetworkPopupService, private _vfModulePopupService : VfModulePopuopService, + private _vfModuleUpgradePopupService : VfModuleUpgradePopupService, private _vnfGroupPopupService : VnfGroupPopupService, private _duplicateService : DuplicateService, private _modalService: SdcUiServices.ModalService, @@ -42,6 +45,7 @@ export class ObjectToTreeService { private _networkStepService : NetworkStepService, private _vpnStepService : VpnStepService, private _aaiService : AaiService, + private _featureFlagsService: FeatureFlagsService, private _store : NgRedux<AppState>) { } @@ -52,10 +56,10 @@ export class ObjectToTreeService { * return all first optional first level of the model tree ************************************************************/ getFirstLevelOptions(): ILevelNodeInfo[] { - return [new VnfModelInfo(this._dynamicInputsService, this._sharedTreeService, this._defaultDataGeneratorService, this._dialogService, this._vnfPopupService, this._vfModulePopupService, this._duplicateService, this._modalService, this._iframeService, this._componentInfoService, this._store) - , new NetworkModelInfo(this._dynamicInputsService, this._sharedTreeService, this._dialogService, this._networkPopupService, this._duplicateService, this._modalService, this._iframeService, this._store), + 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 PnfModelInfo(), - new VrfModelInfo(this._store, this._sharedTreeService, this._dialogService, this._iframeService, this._networkStepService, this._vpnStepService), + 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), new VnfGroupingModelInfo(this._dynamicInputsService, this._sharedTreeService, this._dialogService, this._vnfGroupPopupService, this._iframeService, this._aaiService, this._store)]; diff --git a/vid-webpack-master/src/app/drawingBoard/service-planning/objectsToTree/shared.tree.service.spec.ts b/vid-webpack-master/src/app/drawingBoard/service-planning/objectsToTree/shared.tree.service.spec.ts index 89e20a2d6..b330b72dc 100644 --- a/vid-webpack-master/src/app/drawingBoard/service-planning/objectsToTree/shared.tree.service.spec.ts +++ b/vid-webpack-master/src/app/drawingBoard/service-planning/objectsToTree/shared.tree.service.spec.ts @@ -40,6 +40,7 @@ import {ComponentInfoModel, ComponentInfoType} from "../component-info/component import {ModelInformationItem} from "../../../shared/components/model-information/model-information.component"; import {VpnStepService} from "./models/vrf/vrfModal/vpnStep/vpn.step.service"; import {NetworkStepService} from "./models/vrf/vrfModal/networkStep/network.step.service"; +import {VfModuleUpgradePopupService} from "../../../shared/components/genericFormPopup/genericFormServices/vfModuleUpgrade/vfModule.upgrade.popuop.service"; class MockAppStore<T> { getState() { @@ -96,6 +97,7 @@ describe('Shared Tree Service', () => { NetworkPopupService, NetworkControlGenerator, VfModulePopuopService, + VfModuleUpgradePopupService, VfModuleControlGenerator, VnfGroupControlGenerator, DialogService, @@ -162,7 +164,7 @@ describe('Shared Tree Service', () => { jest.spyOn(AuditInfoModalComponent.openInstanceAuditInfoModal, 'next'); let modelInfoServiceMock: ILevelNodeInfo = new VnfModelInfo(null, null, - null, null, null, null, + null, null, null, null, null, null, null, null, null,null); const modelMock = {"a": "a"}; const instanceMock = {"instance": "instance", "trackById": "123456789"}; @@ -465,19 +467,15 @@ function getStore() { "global": { "name": null, "flags": { - "CREATE_INSTANCE_TEST": false, "EMPTY_DRAWING_BOARD_TEST": false, "FLAG_NETWORK_TO_ASYNC_INSTANTIATION": false, "FLAG_ADD_MSO_TESTAPI_FIELD": true, "FLAG_SERVICE_MODEL_CACHE": true, "FLAG_SHOW_ASSIGNMENTS": true, "FLAG_FABRIC_CONFIGURATION_ASSIGNMENTS": true, - "FLAG_DEFAULT_VNF": true, - "FLAG_SETTING_DEFAULTS_IN_DRAWING_BOARD": true, "FLAG_A_LA_CARTE_AUDIT_INFO": true, "FLAG_1810_CR_ADD_CLOUD_OWNER_TO_MSO_REQUEST": true, "FLAG_PRESENT_PROVIDER_NETWORKS_ASSOCIATIONS": true, - "FLAG_1810_CR_SOFT_DELETE_ALACARTE_VF_MODULE": true, "FLAG_1902_NEW_VIEW_EDIT": true, "FLAG_1810_IDENTIFY_SERVICE_FOR_NEW_UI": false, "FLAG_1902_VNF_GROUPING": true, diff --git a/vid-webpack-master/src/app/drawingBoard/service-planning/objectsToTree/shared.tree.service.ts b/vid-webpack-master/src/app/drawingBoard/service-planning/objectsToTree/shared.tree.service.ts index c56cc4999..b8eddbbf7 100644 --- a/vid-webpack-master/src/app/drawingBoard/service-planning/objectsToTree/shared.tree.service.ts +++ b/vid-webpack-master/src/app/drawingBoard/service-planning/objectsToTree/shared.tree.service.ts @@ -13,6 +13,7 @@ import {ModelInformationItem} from "../../../shared/components/model-information import {undoUpgradeService, upgradeService} from "../../../shared/storeUtil/utils/service/service.actions"; import {VNFMethods} from "../../../shared/storeUtil/utils/vnf/vnf.actions"; import {FeatureFlagsService, Features} from "../../../shared/services/featureFlag/feature-flags.service"; +import {Utils} from "../../../shared/utils/utils"; @Injectable() export class SharedTreeService { @@ -239,6 +240,16 @@ export class SharedTreeService { const mode = this._store.getState().global.drawingBoardStatus; return mode === DrawingBoardModes.EDIT || mode=== DrawingBoardModes.CREATE; } + + + isReachedToMaxInstances(properties, counter, flags): boolean{ + let maxInstances = Utils.getMaxFirstLevel(properties, flags); + if(_.isNil(maxInstances)){ + return false; + }else { + return !(maxInstances > counter); + } + } /************************************************ return number of instances with action Delete @type: vnfs networks, vngGroups (not vfModule) diff --git a/vid-webpack-master/src/app/instantiationStatus/instantiationStatus.component.html b/vid-webpack-master/src/app/instantiationStatus/instantiationStatus.component.html index 212981aaf..16b8c0132 100644 --- a/vid-webpack-master/src/app/instantiationStatus/instantiationStatus.component.html +++ b/vid-webpack-master/src/app/instantiationStatus/instantiationStatus.component.html @@ -16,6 +16,13 @@ <span class="icon-refresh"></span> </div> </div> + <div class="instantiationStatusFilter" *ngIf=isInstantiationStatusFilterFlagOn()> + <input + [attr.data-tests-id]="'instantiation-status-filter'" + class="form-control input-text" + [placeholder]="'filter'" + [(ngModel)]="filterText"> + </div> </div> </div> </div> @@ -41,51 +48,51 @@ </tr> </thead> <tbody > - <tr *ngFor="let data of serviceInfoData; trackBy: trackByFn; let i = index" [ngClass]="{'odd' : data.serviceIndex%2 == 1}" [id]="data.jobId"> - <td class="smallTd" id="userId"><custom-ellipsis [id]="data.userId" [value]="data.userId"></custom-ellipsis></td> - <td class="smallTd" id="action"><custom-ellipsis [id]="data.action" [value]="data.action | capitalizeAndFormat"></custom-ellipsis></td> - <td class="normal" id="serviceModelName"><custom-ellipsis [id]="data.serviceModelName" [value]="data.serviceModelName"></custom-ellipsis></td> - <td class="normal" id="serviceInstanceName"><custom-ellipsis [id]="data.serviceInstanceName" [value]="data.serviceInstanceName"></custom-ellipsis></td> - <td class="smallTd" id="serviceModelVersion"><custom-ellipsis [id]="data.serviceModelVersion" [value]="data.serviceModelVersion"></custom-ellipsis></td> - <td class="normal" id="subscriberName"><custom-ellipsis [id]="data.subscriberName" [value]="data.subscriberName"></custom-ellipsis></td> - <td class="mediumTd" id="serviceType"><custom-ellipsis [id]="data.serviceType" [value]="data.serviceType"></custom-ellipsis></td> - <td class="normal" id="regionId"><custom-ellipsis [id]="data.regionId" [value]="data.regionId"></custom-ellipsis></td> - <td class="mediumTd" id="tenantName"><custom-ellipsis [id]="data.tenantName" [value]="data.tenantName"></custom-ellipsis></td> - <td class="mediumTd" id="aicZoneName"><custom-ellipsis [id]="data.aicZoneName" [value]="data.aicZoneName"></custom-ellipsis></td> - <td class="mediumTd" id="project"><custom-ellipsis [id]="data.project" [value]="data.project"></custom-ellipsis></td> - <td class="mediumTd" id="owningEntityName"><custom-ellipsis [id]="data.owningEntityName" [value]="data.owningEntityName"></custom-ellipsis></td> - <td class="smallTd" id="pause"><custom-ellipsis [id]="data.pause" [value]="data.pause"></custom-ellipsis></td> - <td class="mediumTd" id="created"><custom-ellipsis [id]="data.created" [value]="data.created | date:'MMM. dd, yyyy HH:mm'"></custom-ellipsis></td> - <td class="last" id="jobStatus" [ngClass]="data.jobStatus"> - <custom-popover [value]="data.serviceStatus.tooltip" [popoverType]="data?.serviceStatus?.color" style="float: left;"> - <svg-icon - id="jobStatusIcon-{{i}}" - (click)="auditInfo(data)" - [mode]="data.serviceStatus.color" - [size]="'large'" - [name]="data.serviceStatus.iconClassName"> - </svg-icon> + <tr *ngFor="let data of serviceInfoData | searchFilter: filterText ; trackBy: trackByFn; let i = index" [ngClass]="{'odd' : data.serviceIndex%2 == 1}" [id]="data.jobId"> + <td class="smallTd" id="userId"><custom-ellipsis [id]="data.userId" [value]="data.userId"></custom-ellipsis></td> + <td class="smallTd" id="action"><custom-ellipsis [id]="data.action" [value]="data.action | capitalizeAndFormat"></custom-ellipsis></td> + <td class="normal" id="serviceModelName"><custom-ellipsis [id]="data.serviceModelName" [value]="data.serviceModelName"></custom-ellipsis></td> + <td class="normal" id="serviceInstanceName"><custom-ellipsis [id]="data.serviceInstanceName" [value]="data.serviceInstanceName"></custom-ellipsis></td> + <td class="smallTd" id="serviceModelVersion"><custom-ellipsis [id]="data.serviceModelVersion" [value]="data.serviceModelVersion"></custom-ellipsis></td> + <td class="normal" id="subscriberName"><custom-ellipsis [id]="data.subscriberName" [value]="data.subscriberName"></custom-ellipsis></td> + <td class="mediumTd" id="serviceType"><custom-ellipsis [id]="data.serviceType" [value]="data.serviceType"></custom-ellipsis></td> + <td class="normal" id="regionId"><custom-ellipsis [id]="data.regionId" [value]="data.regionId"></custom-ellipsis></td> + <td class="mediumTd" id="tenantName"><custom-ellipsis [id]="data.tenantName" [value]="data.tenantName"></custom-ellipsis></td> + <td class="mediumTd" id="aicZoneName"><custom-ellipsis [id]="data.aicZoneName" [value]="data.aicZoneName"></custom-ellipsis></td> + <td class="mediumTd" id="project"><custom-ellipsis [id]="data.project" [value]="data.project"></custom-ellipsis></td> + <td class="mediumTd" id="owningEntityName"><custom-ellipsis [id]="data.owningEntityName" [value]="data.owningEntityName"></custom-ellipsis></td> + <td class="smallTd" id="pause"><custom-ellipsis [id]="data.pause" [value]="data.pause"></custom-ellipsis></td> + <td class="mediumTd" id="created"><custom-ellipsis [id]="data.created" [value]="data.created | date:'MMM. dd, yyyy HH:mm'"></custom-ellipsis></td> + <td class="last" id="jobStatus" [ngClass]="data.jobStatus"> + <custom-popover [value]="data.serviceStatus.tooltip" [popoverType]="data?.serviceStatus?.color" style="float: left;"> + <svg-icon + id="jobStatusIcon-{{i}}" + (click)="auditInfo(data)" + [mode]="data.serviceStatus.color" + [size]="'large'" + [name]="data.serviceStatus.iconClassName"> + </svg-icon> - </custom-popover> - <div class="menu-div" (click)="onContextMenu($event, data)"> - <span class="icon-menu"></span> - <context-menu> - <ng-template *ngFor="let action of contextMenuActions" contextMenuItem let-item - [visible]="action.visible" - [enabled]="action.enabled" - (execute)="action.click($event.item)"> - <div [attr.data-tests-id]="action.dataTestId" - [tooltip]="action?.tooltip" - [tooltipDisabled]="!action.tooltip"> + </custom-popover> + <div class="menu-div" (click)="onContextMenu($event, data)" [attr.data-tests-id]="'menu-'+data.jobId"> + <span class="icon-menu"></span> + <context-menu> + <ng-template *ngFor="let action of contextMenuActions" contextMenuItem let-item + [visible]="action.visible" + [enabled]="action.enabled" + (execute)="action.click($event.item)"> + <div [attr.data-tests-id]="action.dataTestId" + [tooltip]="action?.tooltip" + [tooltipDisabled]="!action.tooltip"> <span class="context-menu-icon"> <i class="fa {{action.className}}" aria-hidden="true"></i> </span> - {{action.name}} - </div> - </ng-template> - </context-menu> - </div> - </td> + {{action.name}} + </div> + </ng-template> + </context-menu> + </div> + </td> </tr> </tbody> </table> diff --git a/vid-webpack-master/src/app/instantiationStatus/instantiationStatus.component.scss b/vid-webpack-master/src/app/instantiationStatus/instantiationStatus.component.scss index 65c2400a3..352a7db10 100644 --- a/vid-webpack-master/src/app/instantiationStatus/instantiationStatus.component.scss +++ b/vid-webpack-master/src/app/instantiationStatus/instantiationStatus.component.scss @@ -275,3 +275,9 @@ div.dataTables_wrapper { margin-top: 0px; height: 0; } + +.instantiationStatusFilter { + position: absolute; + right: 20px; + width: 22%; +} diff --git a/vid-webpack-master/src/app/instantiationStatus/instantiationStatus.component.service.spec.ts b/vid-webpack-master/src/app/instantiationStatus/instantiationStatus.component.service.spec.ts index 27d3f419b..eedd46dd3 100644 --- a/vid-webpack-master/src/app/instantiationStatus/instantiationStatus.component.service.spec.ts +++ b/vid-webpack-master/src/app/instantiationStatus/instantiationStatus.component.service.spec.ts @@ -1,49 +1,29 @@ import {getTestBed, TestBed} from '@angular/core/testing'; -import { - COMPLETED_WITH_ERRORS, - INPROGRESS, - InstantiationStatusComponentService, - PAUSE, - PENDING, - ServiceStatus, - STOPPED, - SUCCESS_CIRCLE, - UNKNOWN, - X_O -} from './instantiationStatus.component.service'; +import {COMPLETED_WITH_ERRORS, INPROGRESS, InstantiationStatusComponentService, PAUSE, PENDING, ServiceStatus, STOPPED, SUCCESS_CIRCLE, UNKNOWN, X_O} from './instantiationStatus.component.service'; import {ServiceInfoModel} from '../shared/server/serviceInfo/serviceInfo.model'; import {AaiService} from "../shared/services/aaiService/aai.service"; import {MsoService} from "../shared/services/msoService/mso.service"; import {NgRedux} from "@angular-redux/store"; import {HttpClientTestingModule} from "@angular/common/http/testing"; -import {FeatureFlagsService} from "../shared/services/featureFlag/feature-flags.service"; +import {FeatureFlagsService, Features} from "../shared/services/featureFlag/feature-flags.service"; import {DrawingBoardModes} from "../drawingBoard/service-planning/drawing-board.modes"; import {RouterTestingModule} from "@angular/router/testing"; import {of} from "rxjs"; import {UrlTree} from "@angular/router"; +import each from "jest-each"; +import {ServiceAction} from "../shared/models/serviceInstanceActions"; +import {instance, mock, when} from "ts-mockito"; class MockAppStore<T> { - - getState() { - return { - global: { - flags: { - 'FLAG_1902_NEW_VIEW_EDIT': true, - - } - } - } - } - - dispatch() { - - } + dispatch() {} } + describe('Instantiation Status Service', () => { let injector; let aaiService: AaiService; let msoService: MsoService; let service: InstantiationStatusComponentService; + let mockFeatureFlagsService: FeatureFlagsService = mock(FeatureFlagsService); beforeAll(done => (async () => { @@ -57,7 +37,9 @@ describe('Instantiation Status Service', () => { AaiService, MsoService, FeatureFlagsService, - {provide: NgRedux, useClass: MockAppStore}] + {provide: NgRedux, useClass: MockAppStore}, + {provide: FeatureFlagsService, useValue: instance(mockFeatureFlagsService)} + ] }); await TestBed.compileComponents(); @@ -101,33 +83,48 @@ describe('Instantiation Status Service', () => { }); }); - test('click on "Open" button should open new view edit' , ()=>{ - const item = { - serviceModelId : 'serviceModelId', - serviceInstanceId : 'serviceInstanceId', - serviceType : 'serviceType', - subscriberId : 'subscriberId' - }; - let params:UrlTree = service.getNewViewEditUrlTree(<any>item, DrawingBoardModes.VIEW); - expect(params.toString().startsWith('/servicePlanning/VIEW')).toBeTruthy(); - expect(params.queryParams).toEqual( - { - serviceModelId: item.serviceModelId, - serviceInstanceId: item.serviceInstanceId, - serviceType : item.serviceType, - subscriberId : item.subscriberId - }); - }); + describe('navigations tests:', () => { - test('build the View Edit url' , ()=>{ const item = { - serviceModelId : '28aeb8f6-5620-4148-8bfb-a5fb406f0309', + serviceModelId: '28aeb8f6-5620-4148-8bfb-a5fb406f0309', + serviceInstanceId: 'myInstanceId', + serviceType: 'myService', + subscriberId: 'mySubscriber', + jobId: 'aJobId' }; - let serviceModelUrl: string = '/servicePlanning/EDIT?serviceModelId=28aeb8f6-5620-4148-8bfb-a5fb406f0309'; - let suffix:string = '../../serviceModels.htm#'; - let tree:UrlTree = service.getNewViewEditUrlTree(<any>item, DrawingBoardModes.EDIT); - let result = service.getViewEditUrl(tree); - expect (suffix + serviceModelUrl).toEqual(result); + + test('click on "Open" button should open new view edit', () => { + let params: UrlTree = service.getNewViewEditUrlTree(<any>item, DrawingBoardModes.VIEW); + expect(params.toString().startsWith('/servicePlanning/VIEW')).toBeTruthy(); + expect(params.queryParams).toEqual( + { + serviceModelId: item.serviceModelId, + serviceInstanceId: item.serviceInstanceId, + serviceType: item.serviceType, + subscriberId: item.subscriberId, + jobId: item.jobId + }); + }); + + test('build the View Edit url', () => { + + let serviceModelUrl: string = '/servicePlanning/EDIT?serviceModelId=28aeb8f6-5620-4148-8bfb-a5fb406f0309' + + '&serviceInstanceId=myInstanceId&serviceType=myService&subscriberId=mySubscriber&jobId=aJobId'; + let prefix: string = '../../serviceModels.htm#'; + let tree: UrlTree = service.getNewViewEditUrlTree(<any>item, DrawingBoardModes.EDIT); + let result = service.getViewEditUrl(tree); + expect(result).toEqual(prefix + serviceModelUrl); + }); + + test('recreate url shall contains mode RECREATE and only jobId and serviceModelId', () =>{ + let params: UrlTree = service.getNewViewEditUrlTree(<any>item, DrawingBoardModes.RECREATE); + expect(params.toString().startsWith('/servicePlanning/RECREATE')).toBeTruthy(); + expect(params.queryParams).toEqual( + { + serviceModelId: item.serviceModelId, + jobId: item.jobId + }); + }); }); for (let [status, tooltip] of Object.entries({ @@ -154,6 +151,27 @@ describe('Instantiation Status Service', () => { expect(statusResult.iconClassName).toEqual(UNKNOWN); }); + + each([ + [true, ServiceAction.INSTANTIATE], + [false, ServiceAction.UPDATE], + [false, ServiceAction.DELETE], + ]). + test('isRecreateEnabled: should be %s if action is %s', (expected:boolean, action:ServiceAction) => { + let serviceInfoModel = new ServiceInfoModel(); + serviceInfoModel.action = action; + expect(service.isRecreateEnabled(serviceInfoModel)).toBe(expected); + }); + + each([ + [true, true], + [false, false], + ]). + test('isRecreateVisible: should be %s if flag is %s', (expected:boolean, flag:boolean) => { + when(mockFeatureFlagsService.getFlagState(Features.FLAG_2004_CREATE_ANOTHER_INSTANCE_FROM_TEMPLATE)).thenReturn(flag); + expect(service.isRecreateVisible()).toEqual(expected); + }); + test('getStatusTooltip should return correct icon per job status', () => { let result : ServiceStatus = service.getStatus('pending'); expect(result.iconClassName).toEqual(PENDING); diff --git a/vid-webpack-master/src/app/instantiationStatus/instantiationStatus.component.service.ts b/vid-webpack-master/src/app/instantiationStatus/instantiationStatus.component.service.ts index ffc2e681a..4bfedd913 100644 --- a/vid-webpack-master/src/app/instantiationStatus/instantiationStatus.component.service.ts +++ b/vid-webpack-master/src/app/instantiationStatus/instantiationStatus.component.service.ts @@ -12,6 +12,7 @@ import {updateDrawingBoardStatus} from "../shared/storeUtil/utils/global/global. import {Router, UrlTree} from "@angular/router"; import {of} from "rxjs"; import {MsoService} from "../shared/services/msoService/mso.service"; +import {ServiceAction} from "../shared/models/serviceInstanceActions"; export let PENDING : string = "pending"; export let INPROGRESS : string = "in_progress"; @@ -28,7 +29,8 @@ export class InstantiationStatusComponentService { constructor( private _aaiService: AaiService, private _msoService: MsoService, private _router : Router, - private _store: NgRedux<AppState>) { + private _store: NgRedux<AppState>, + private _featureFlagsService:FeatureFlagsService) { } generateServiceInfoDataMapping(arr: ServiceInfoModel[]) : { [serviceInstanceId: string]: ServiceInfoModel[]}{ @@ -69,7 +71,7 @@ export class InstantiationStatusComponentService { } open(item: ServiceInfoModel): void { - if (FeatureFlagsService.getFlagState(Features.FLAG_1902_VNF_GROUPING, this._store)) { + if (this._featureFlagsService.getFlagState(Features.FLAG_1902_VNF_GROUPING)) { this._aaiService.getServiceModelById(item['serviceModelId']).subscribe((result)=>{ const serviceModel = new ServiceModel(result); @@ -91,7 +93,7 @@ export class InstantiationStatusComponentService { } navigateToNewViewOnlyOrOldEditView(item: ServiceInfoModel) { - if (FeatureFlagsService.getFlagState(Features.FLAG_1902_NEW_VIEW_EDIT, this._store)) { + if (this._featureFlagsService.getFlagState(Features.FLAG_1902_NEW_VIEW_EDIT)) { this.navigateToNewViewEdit(item, DrawingBoardModes.VIEW); } else { @@ -122,19 +124,31 @@ export class InstantiationStatusComponentService { ['/servicePlanning/' + mode], { queryParams: - { - serviceModelId: item.serviceModelId, - serviceInstanceId: item.serviceInstanceId, - serviceType : item.serviceType, - subscriberId : item.subscriberId, - jobId: item.jobId - } + mode==DrawingBoardModes.RECREATE ? + this.getRecreateQueryParams(item) : + this.getDefaultViewEditQueryParams(item) }); } + private getDefaultViewEditQueryParams(item: ServiceInfoModel) { + return { + serviceModelId: item.serviceModelId, + serviceInstanceId: item.serviceInstanceId, + serviceType: item.serviceType, + subscriberId: item.subscriberId, + jobId: item.jobId + }; + } + + private getRecreateQueryParams(item: ServiceInfoModel) { + return { + serviceModelId: item.serviceModelId, + jobId: item.jobId + }; + } + getViewEditUrl(viewEditUrlTree:UrlTree): string { return '../../serviceModels.htm#' + viewEditUrlTree.toString(); - } getStatus(status : string) : ServiceStatus { @@ -162,6 +176,18 @@ export class InstantiationStatusComponentService { retry(item: ServiceInfoModel): void { this.navigateToNewViewEdit(item, DrawingBoardModes.RETRY_EDIT); } + + recreate(item: ServiceInfoModel): void { + this.navigateToNewViewEdit(item, DrawingBoardModes.RECREATE); + } + + isRecreateEnabled(item: ServiceInfoModel): boolean { + return item.action === ServiceAction.INSTANTIATE; + } + + isRecreateVisible(): boolean { + return this._featureFlagsService.getFlagState(Features.FLAG_2004_CREATE_ANOTHER_INSTANCE_FROM_TEMPLATE); + } } diff --git a/vid-webpack-master/src/app/instantiationStatus/instantiationStatus.component.spec.ts b/vid-webpack-master/src/app/instantiationStatus/instantiationStatus.component.spec.ts index 53dfcc1f2..e6ccdda8e 100644 --- a/vid-webpack-master/src/app/instantiationStatus/instantiationStatus.component.spec.ts +++ b/vid-webpack-master/src/app/instantiationStatus/instantiationStatus.component.spec.ts @@ -17,7 +17,10 @@ import {FeatureFlagsService} from "../shared/services/featureFlag/feature-flags. import {JobStatus, ServiceAction} from "../shared/models/serviceInstanceActions"; import each from 'jest-each'; import {ServiceInfoModel} from "../shared/server/serviceInfo/serviceInfo.model"; -import { TooltipModule } from 'ngx-tooltip'; +import {TooltipModule} from 'ngx-tooltip'; +import {SearchFilterPipe} from "../shared/pipes/searchFilter/search-filter.pipe"; +import {ActivatedRoute} from "@angular/router"; +import {FormsModule} from "@angular/forms"; class MockAppStore<T> { @@ -36,10 +39,21 @@ class MockAppStore<T> { } } +class ActivatedRouteMock<T>{ + queryParams() { + return {} + }; + + snapshot = { + queryParams : {} + } +} + describe('Instantiation Status Component', () => { let component: InstantiationStatusComponent; let fixture: ComponentFixture<InstantiationStatusComponent>; -let item = new ServiceInfoModel(); + let item = new ServiceInfoModel(); + beforeAll(done => (async () => { TestBed.configureTestingModule({ @@ -48,7 +62,8 @@ let item = new ServiceInfoModel(); ContextMenuModule, ScrollToModule.forRoot(), RouterTestingModule, - TooltipModule + TooltipModule, + FormsModule, ], providers: [ ServiceInfoService, @@ -59,9 +74,10 @@ let item = new ServiceInfoModel(); FeatureFlagsService, ConfigurationService, LogService, + {provide: ActivatedRoute, useClass: ActivatedRouteMock}, {provide: NgRedux, useClass: MockAppStore} ], - declarations: [InstantiationStatusComponent, CapitalizeAndFormatPipe], + declarations: [InstantiationStatusComponent, CapitalizeAndFormatPipe, SearchFilterPipe], schemas: [ CUSTOM_ELEMENTS_SCHEMA ] }); await TestBed.compileComponents(); diff --git a/vid-webpack-master/src/app/instantiationStatus/instantiationStatus.component.ts b/vid-webpack-master/src/app/instantiationStatus/instantiationStatus.component.ts index f3f651960..58227c9eb 100644 --- a/vid-webpack-master/src/app/instantiationStatus/instantiationStatus.component.ts +++ b/vid-webpack-master/src/app/instantiationStatus/instantiationStatus.component.ts @@ -1,4 +1,4 @@ -import {Component, ViewChild} from '@angular/core'; +import {Component, OnInit, ViewChild} from '@angular/core'; import {ServiceInfoService} from '../shared/server/serviceInfo/serviceInfo.service'; import {ServiceInfoModel} from '../shared/server/serviceInfo/serviceInfo.model'; import {InstantiationStatusComponentService} from './instantiationStatus.component.service'; @@ -11,7 +11,8 @@ import {LogService} from '../shared/utils/log/log.service'; import {AppState} from "../shared/store/reducers"; import {NgRedux} from '@angular-redux/store'; import {JobStatus, ServiceAction} from "../shared/models/serviceInstanceActions"; -import {DrawingBoardModes} from "../drawingBoard/service-planning/drawing-board.modes"; +import {ActivatedRoute} from "@angular/router"; +import {FeatureFlagsService, Features} from "../shared/services/featureFlag/feature-flags.service"; export interface MenuAction{ name: string; @@ -28,7 +29,7 @@ export interface MenuAction{ templateUrl : './instantiationStatus.component.html', styleUrls : ['./instantiationStatus.component.scss'] }) -export class InstantiationStatusComponent { +export class InstantiationStatusComponent implements OnInit { TIMER_TIME_IN_SECONDS : number = 0; timer = null; @@ -58,6 +59,14 @@ export class InstantiationStatusComponent { visible: () => true, }, { + name: "Recreate", + dataTestId: "context-menu-recreate", + className: "fa-clone", + click: (item: ServiceInfoModel) => this.instantiationStatusComponentService.recreate(item), + enabled: (item: ServiceInfoModel) => this.instantiationStatusComponentService.isRecreateEnabled(item), + visible: () => this.instantiationStatusComponentService.isRecreateVisible(), + }, + { name: "Audit info", dataTestId: "context-menu-audit-info", className: "fa-info-circle", @@ -85,12 +94,14 @@ export class InstantiationStatusComponent { ]; flags: any; + filterText: string; constructor(private _serviceInfoService: ServiceInfoService, private _instantiationStatusComponentService : InstantiationStatusComponentService, private _contextMenuService: ContextMenuService, private _configurationService : ConfigurationService, private _scrollToService: ScrollToService, private _logService : LogService, + private route: ActivatedRoute, private _store: NgRedux<AppState>) { this.instantiationStatusComponentService = _instantiationStatusComponentService; this.configurationService = this._configurationService; @@ -101,6 +112,11 @@ export class InstantiationStatusComponent { }); } + ngOnInit() { + let filterTextParam = this.route.snapshot.queryParams["filterText"]; + this.filterText = filterTextParam ? filterTextParam : "" ; + } + activateInterval() { if (this.TIMER_TIME_IN_SECONDS > 0) { this.timer = setInterval(() => { @@ -115,7 +131,7 @@ export class InstantiationStatusComponent { refreshData(): void { this.dataIsReady = false; - this._serviceInfoService.getServicesJobInfo(true, this.lastUpdatedDate === null) + this._serviceInfoService.getServicesJobInfo(this.lastUpdatedDate === null) .subscribe((res: ServiceInfoModel[]) => { this._instantiationStatusComponentService.convertObjectToArray(res).subscribe((res) => { this._logService.info('refresh instantiation status table', res); @@ -144,7 +160,7 @@ export class InstantiationStatusComponent { this.refreshData(); }); } - + retryItem(item: ServiceInfoModel) : void { if (item.isRetryEnabled) { this._instantiationStatusComponentService.retry(item); @@ -230,4 +246,8 @@ export class InstantiationStatusComponent { }, 0) } } + + isInstantiationStatusFilterFlagOn() { + return FeatureFlagsService.getFlagState(Features.FLAG_2004_INSTANTIATION_STATUS_FILTER, this._store); + } } diff --git a/vid-webpack-master/src/app/shared/components/ellipsis/ellipsis.component.ts b/vid-webpack-master/src/app/shared/components/ellipsis/ellipsis.component.ts index 07f4d481b..a7b8ac744 100644 --- a/vid-webpack-master/src/app/shared/components/ellipsis/ellipsis.component.ts +++ b/vid-webpack-master/src/app/shared/components/ellipsis/ellipsis.component.ts @@ -3,18 +3,20 @@ import {HighlightPipe} from "../../pipes/highlight/highlight-filter.pipe"; import * as _ from 'lodash'; @Component({ - selector : 'custom-ellipsis', + selector: 'custom-ellipsis', template: ` <span - sdc-tooltip - class="ellipsis" - id="{{id}}" - [innerHtml]="displayValue | safe : 'html'" - [ngClass]="{'breakWord' : breakWord == true}" - [tooltip-text]="value"> + sdc-tooltip + class="ellipsis" + [attr.data-tests-id]="dataTestId" + id="{{id}}" + [innerHtml]="displayValue | safe : 'html'" + [ngStyle]="{'white-space' : showDots ? 'nowrap' : 'initial'}" + [ngClass]="{'breakWord' : breakWord == true}" + [tooltip-text]="value"> </span>`, - styles : [ - ` + styles: [ + ` .ellipsis { white-space: nowrap; overflow: hidden; @@ -23,30 +25,33 @@ import * as _ from 'lodash'; width: 99%; text-align: left; } - + .breakWord { word-wrap: break-word; white-space: normal; } ` ], - providers : [HighlightPipe] + providers: [HighlightPipe] }) -export class EllipsisComponent implements OnChanges{ - @Input() value : string; - @Input() id : string; - @Input() hightlight : string; - @Input() breakWord : boolean = false; +export class EllipsisComponent implements OnChanges { + @Input() value: string; + @Input() id: string; + @Input() hightlight: string; + @Input() breakWord: boolean = false; + @Input() dataTestId: string; + @Input() showDots: boolean = false; + + displayValue: string; - displayValue : string; - constructor(private _highlightPipe : HighlightPipe){ + constructor(private _highlightPipe: HighlightPipe) { this.displayValue = this.value; } ngOnChanges(changes: SimpleChanges): void { this.displayValue = this.value; - if(!_.isNil(this.hightlight)){ - this.displayValue = this._highlightPipe.transform(this.value ,this.hightlight ? this.hightlight : ''); + if (!_.isNil(this.hightlight)) { + this.displayValue = this._highlightPipe.transform(this.value, this.hightlight ? this.hightlight : ''); } } } diff --git a/vid-webpack-master/src/app/shared/components/formControls/component/multiselect/multiselect.formControl.component.html b/vid-webpack-master/src/app/shared/components/formControls/component/multiselect/multiselect.formControl.component.html index f7c4894b2..f205259e4 100644 --- a/vid-webpack-master/src/app/shared/components/formControls/component/multiselect/multiselect.formControl.component.html +++ b/vid-webpack-master/src/app/shared/components/formControls/component/multiselect/multiselect.formControl.component.html @@ -1,16 +1,26 @@ -<div> - <div class="details-item" *ngIf="data != null && form != null"> - <label [ngClass]="{'required': data.isRequired()}" - for="{{data?.dataTestId}}">{{data?.displayName}}:</label> - <angular2-multiselect id="{{data?.dataTestId}}" - [attr.data-tests-id]="data?.dataTestId" - [formControl]="form.controls[data.controlName]" - [(ngModel)]="data.selectedItems" - [data]="data?.options$" - [settings]="data?.settings" - title="{{data.tooltip}}" - [ngClass]="{'error-style' : form?.controls[data?.controlName]?.touched && form?.controls[data?.controlName]?.errors}"> - </angular2-multiselect> - </div> -</div> +<div class="details-item" *ngIf="data != null && form != null"> + <label [ngClass]="{'required': data.isRequired()}" + for="{{data?.dataTestId}}-select">{{data?.displayName}}:</label> + + <angular2-multiselect + [attr.data-tests-id]="data?.dataTestId" + [data]="options" + [(ngModel)]="selectedItems" + [settings]="dropdownSettings" + (onSelect)="onItemSelect($event)" + (onDeSelect)="OnItemDeSelect($event)" + (onSelectAll)="onSelectAll($event)" + (onDeSelectAll)="onDeSelectAll($event)">> + <c-item> + <ng-template let-item="item"> + <label + [attr.data-tests-id]="data.dataTestId + '-' + item?.itemName" + style="color: #333;min-width: 150px;">{{item?.itemName}}</label> + </ng-template> + </c-item> + </angular2-multiselect> + + <form-control-error *ngIf="data?.hasEmptyOptions && data?.isRequired()" + [message]="'No results for this request. Please change criteria.'"></form-control-error> +</div> diff --git a/vid-webpack-master/src/app/shared/components/formControls/component/multiselect/multiselect.formControl.component.spec.ts b/vid-webpack-master/src/app/shared/components/formControls/component/multiselect/multiselect.formControl.component.spec.ts deleted file mode 100644 index 81c8d4679..000000000 --- a/vid-webpack-master/src/app/shared/components/formControls/component/multiselect/multiselect.formControl.component.spec.ts +++ /dev/null @@ -1,74 +0,0 @@ -import {ComponentFixture, TestBed} from '@angular/core/testing'; -import {CUSTOM_ELEMENTS_SCHEMA} from '@angular/core' -import {CommonModule} from "@angular/common"; -import {FormBuilder, FormControl, ReactiveFormsModule, Validators} from "@angular/forms"; -import { - ValidatorModel, - ValidatorOptions -} from "../../../../models/formControlModels/formControl.model"; -import {FormControlMessageErrorComponent} from "../../errorMessage/formControlMessageError.component"; -import {BrowserModule} from "@angular/platform-browser"; -import {MultiselectFormControlComponent} from "./multiselect.formControl.component"; -import {MultiselectFormControl} from "../../../../models/formControlModels/multiselectFormControl.model"; -import { of } from "rxjs"; -describe('Dropdown Form Control Component', () => { - let component: MultiselectFormControlComponent; - let fixture: ComponentFixture<MultiselectFormControlComponent>; - let fb: FormBuilder; - - beforeAll(done => (async () => { - TestBed.configureTestingModule({ - imports: [CommonModule, BrowserModule, ReactiveFormsModule], - providers: [FormBuilder], - declarations: [MultiselectFormControlComponent, FormControlMessageErrorComponent], - schemas: [CUSTOM_ELEMENTS_SCHEMA] - }); - await TestBed.compileComponents(); - - fixture = TestBed.createComponent(MultiselectFormControlComponent); - component = fixture.componentInstance; - fb = TestBed.get(FormBuilder); - - })().then(done).catch(done.fail)); - - test('component should initialize basic parameters', () => { - component.data = new MultiselectFormControl({ - displayName: "display Name", - validations: [new ValidatorModel(ValidatorOptions.required, 'is required')], - dataTestId: "data-test-id", - placeHolder: "place holder", - controlName: 'testDropdown', - options: of([ - 'option1', - 'option2', - 'option3', - 'onBlur' - ]) - }); - - component.data.hasErrors = function () { - return this.formGroup.controls[this.controlName].touched && this.formGroup.controls[this.controlName].errors ? ['error-style'] : []; - }; - - component.data.onBlur = function () { - component.form.controls['testDropdown'].setValue('onBlur'); - }; - - component.form = fb.group({ - 'testDropdown': new FormControl({ - value: component.data.value, - disabled: false - }, Validators.compose(component.data.validations.map(item => item.validator))) - }); - - component.form.controls['testDropdown'].setValue(''); - expect(component.form.controls['testDropdown'].errors.required).toBeTruthy(); - component.form.controls['testDropdown'].setValue('option2'); - expect(component.form.controls['testDropdown'].errors).toBeFalsy(); - component.data.onBlur(); - expect(component.form.controls['testDropdown'].value).toEqual('onBlur'); - expect(component.form.controls['testDropdown'].errors).toBeFalsy(); - } - ) -}); - diff --git a/vid-webpack-master/src/app/shared/components/formControls/component/multiselect/multiselect.formControl.component.ts b/vid-webpack-master/src/app/shared/components/formControls/component/multiselect/multiselect.formControl.component.ts index 4b98c7e26..26a55e9d2 100644 --- a/vid-webpack-master/src/app/shared/components/formControls/component/multiselect/multiselect.formControl.component.ts +++ b/vid-webpack-master/src/app/shared/components/formControls/component/multiselect/multiselect.formControl.component.ts @@ -1,6 +1,8 @@ import {Component, Input, OnChanges, SimpleChanges} from "@angular/core"; import {FormGroup} from "@angular/forms"; import {MultiselectFormControl} from "../../../../models/formControlModels/multiselectFormControl.model"; +import {MultiselectFormControlService} from "./multiselect.formControl.service"; +import {MultiSelectItem} from "./multiselect.model"; @Component({ selector: 'multiselect-form-control', @@ -8,13 +10,54 @@ import {MultiselectFormControl} from "../../../../models/formControlModels/multi }) export class MultiselectFormControlComponent implements OnChanges{ @Input() data: MultiselectFormControl = null; + @Input() multiselectOptions: [] = null; + @Input() selectedItems = []; @Input() form: FormGroup = null; - ngOnChanges(changes: SimpleChanges): void { + + multiselectFormControlService : MultiselectFormControlService; + constructor(private _multiselectFormControlService : MultiselectFormControlService){ + this.multiselectFormControlService = _multiselectFormControlService; + } + + dropdownSettings = { + singleSelection : false, + limitSelection : 1000 + }; + + options : MultiSelectItem[]; + + + + async ngOnChanges(changes: SimpleChanges) { + if(this.data.options$){ + this._multiselectFormControlService.convertOriginalItems(this.data).then((options)=>{ + this.options = options; + this._multiselectFormControlService.convertSelectedItems(this.data).then((res)=> { + this.selectedItems = res; + this.form.controls[this.data.controlName].setValue(this.selectedItems); + }) + }); + } if (changes["data"] !== undefined && changes["data"].currentValue !== changes["data"].previousValue && changes["data"].firstChange) { - if(this.data.onInit){ + if (this.data.onInit) { + this.dropdownSettings.limitSelection = this.data.limitSelection; this.data.onInit(this.data, this.form); } } } + + onItemSelect(item:any){ + this.data.onChange(this.selectedItems ,this.form); + } + OnItemDeSelect(item:any){ + this.data.onChange(this.selectedItems ,this.form); + } + onSelectAll(items: any){ + this.data.onChange(this.selectedItems ,this.form); + } + onDeSelectAll(items: any){ + this.data.onChange(this.selectedItems ,this.form); + } } + diff --git a/vid-webpack-master/src/app/shared/components/formControls/component/multiselect/multiselect.formControl.service.spec.ts b/vid-webpack-master/src/app/shared/components/formControls/component/multiselect/multiselect.formControl.service.spec.ts new file mode 100644 index 000000000..3c3c344a3 --- /dev/null +++ b/vid-webpack-master/src/app/shared/components/formControls/component/multiselect/multiselect.formControl.service.spec.ts @@ -0,0 +1,159 @@ +import {async, getTestBed, TestBed} from '@angular/core/testing'; +import {MultiselectFormControlService} from "./multiselect.formControl.service"; +import {MultiselectFormControl} from "../../../../models/formControlModels/multiselectFormControl.model"; +import {MultiSelectItem} from "./multiselect.model"; + +describe('Multi Select Form Control Service', () => { + + let injector; + let service: MultiselectFormControlService; + + beforeAll(done => (async () => { + TestBed.configureTestingModule({ + imports: [], + providers: [MultiselectFormControlService] + }); + await TestBed.compileComponents(); + injector = getTestBed(); + service = injector.get(MultiselectFormControlService); + })().then(done).catch(done.fail)); + + + const options = [ + { + id: 'A', + name: 'a' + }, + { + id: 'B', + name: 'b', + keepMe: -42 + }, + { + id: 'C', + name: 'c' + } + ], + selectedFieldName = 'name', + ngValue = 'id'; + + + test('convertOriginalItems should convert options array to <MultiSelectItem> list', async(() => { + let control: MultiselectFormControl = <any>{ + ngValue, + selectedFieldName, + options$: options + }; + + service.convertOriginalItems(control).then((result: MultiSelectItem[]) => { + expect(result).toEqual([ + {"id": 1, "itemId": 'A', "itemName": 'a'}, + {"id": 2, "itemId": 'B', "itemName": 'b'}, + {"id": 3, "itemId": 'C', "itemName": 'c'} + ]); + }); + })); + + test('convertOriginalItems should return empty list when options list is empty', async(() => { + let control: MultiselectFormControl = <any>{ + ngValue, + selectedFieldName, + options$: [] + }; + + service.convertOriginalItems(control).then((result) => { + expect(result).toEqual([]); + }); + })); + + test('convertOptionsToHashMap - should convert any object to hash map with ngValue', async(() => { + + let control: MultiselectFormControl = <any>{ + ngValue, + selectedFieldName, + options$: options + }; + + let map = service.convertOptionsToHashMap(control); + + expect(Object.keys(map)).toHaveLength(3); + expect(map).toEqual({ + 'A': { + id: 'A', + name: 'a', + index: 1 + }, + 'B': { + id: 'B', + name: 'b', + keepMe: -42, + index: 2 + }, + 'C': { + id: 'C', + name: 'c', + index: 3 + } + + }) + })); + + test('convertOptionsToHashMap - should convert any object to hash map with ngValue: empty options', async(() => { + let control: MultiselectFormControl = <any>{ + ngValue, + selectedFieldName, + options$: [] + }; + + let map = service.convertOptionsToHashMap(control); + + expect(Object.keys(map)).toHaveLength(0) + })); + + test('convertSelectedItems - should convert select item to multi select list', async(() => { + let control: MultiselectFormControl = <any>{ + ngValue, + selectedFieldName, + options$: options, + value: ['A', 'C'] + }; + + service.convertSelectedItems(control).then((selectedOptions) => { + expect(selectedOptions).toHaveLength(2); + expect(selectedOptions[0].itemName).toEqual('a'); + expect(selectedOptions[1].itemName).toEqual('c'); + }) + })); + + test('convertSelectedItems - should convert select item to multi select list with special convert function', async(() => { + let control: MultiselectFormControl = <any>{ + ngValue, + selectedFieldName, + options$: options, + value: 'A,C', + convertOriginalDataToArray: (value) => { + return value.split(','); + } + }; + + service.convertSelectedItems(control).then((selectedOptions) => { + expect(selectedOptions).toHaveLength(2); + expect(selectedOptions[0].itemName).toEqual('a'); + expect(selectedOptions[1].itemName).toEqual('c'); + }) + })); + + + test('convertSelectedItems - should return empty list iof value is empty list', async(() => { + let control: MultiselectFormControl = <any>{ + ngValue, + selectedFieldName, + options$: options, + value: [] + }; + + service.convertSelectedItems(control).then((selectedOptions) => { + expect(selectedOptions).toHaveLength(0); + }) + })); +}); diff --git a/vid-webpack-master/src/app/shared/components/formControls/component/multiselect/multiselect.formControl.service.ts b/vid-webpack-master/src/app/shared/components/formControls/component/multiselect/multiselect.formControl.service.ts new file mode 100644 index 000000000..0b50f4d28 --- /dev/null +++ b/vid-webpack-master/src/app/shared/components/formControls/component/multiselect/multiselect.formControl.service.ts @@ -0,0 +1,50 @@ +import {Injectable} from "@angular/core"; +import {MultiselectFormControl} from "../../../../models/formControlModels/multiselectFormControl.model"; +import {MultiSelectItem} from "./multiselect.model"; +import * as _ from "lodash"; + + +@Injectable() +export class MultiselectFormControlService { + + convertOriginalItems = (control : MultiselectFormControl) : Promise<MultiSelectItem[]> => { + return new Promise<MultiSelectItem[]>((resolve) =>{ + let result: MultiSelectItem[] = []; + if(control.options$) { + let index: number = 1; + control.options$.map((originalItems: any) => { + result.push(new MultiSelectItem(index, originalItems[control.ngValue], originalItems[control.selectedFieldName])); + index++; + }); + } + resolve(result); + }) + }; + + + convertOptionsToHashMap = (config : MultiselectFormControl) => { + let index = 1; + return _.reduce(config.options$ , (obj, param: any ) => { + param.index = index; + index++; + obj[param[config.ngValue]] = param; + return obj; + }, {}); + }; + + convertSelectedItems(control : MultiselectFormControl) : Promise<MultiSelectItem[]>{ + return new Promise<MultiSelectItem[]>((resolve) =>{ + let result: MultiSelectItem[] = []; + const hashMap = this.convertOptionsToHashMap(control); + + if(control.options$ && control.value) { + const convertArray = control.convertOriginalDataToArray ? control.convertOriginalDataToArray(control.value) : control.value; + convertArray.map((itemId) => { + const uniqueIdentifier = itemId.trim(); + result.push(new MultiSelectItem(hashMap[uniqueIdentifier].index, hashMap[uniqueIdentifier][control.ngValue], hashMap[uniqueIdentifier][control.selectedFieldName])); + }); + } + resolve(result); + }); + } +} diff --git a/vid-webpack-master/src/app/shared/components/formControls/component/multiselect/multiselect.model.ts b/vid-webpack-master/src/app/shared/components/formControls/component/multiselect/multiselect.model.ts new file mode 100644 index 000000000..a495211d4 --- /dev/null +++ b/vid-webpack-master/src/app/shared/components/formControls/component/multiselect/multiselect.model.ts @@ -0,0 +1,11 @@ +export class MultiSelectItem { + id : number; + itemId : number; + itemName : string; + + constructor(genericId: number, itemId : number, itemName : string){ + this.id = genericId; + this.itemId = itemId; + this.itemName = itemName; + } +} diff --git a/vid-webpack-master/src/app/shared/components/genericForm/formControlsServices/basic.control.generator.spec.ts b/vid-webpack-master/src/app/shared/components/genericForm/formControlsServices/basic.control.generator.spec.ts index fc2eed4ae..077d849e6 100644 --- a/vid-webpack-master/src/app/shared/components/genericForm/formControlsServices/basic.control.generator.spec.ts +++ b/vid-webpack-master/src/app/shared/components/genericForm/formControlsServices/basic.control.generator.spec.ts @@ -5,6 +5,7 @@ import {FeatureFlagsService} from "../../../services/featureFlag/feature-flags.s import {BasicControlGenerator} from "./basic.control.generator"; import {NgRedux} from '@angular-redux/store'; import {HttpClientTestingModule, HttpTestingController} from '@angular/common/http/testing'; +import {FileFormControl} from "../../../models/formControlModels/fileFormControl.model"; class MockAppStore<T> {} @@ -44,5 +45,41 @@ describe('Basic Control Generator', () => { const legacyRegionControl: FormControlModel = service.getLegacyRegion(instance); expect(legacyRegionControl.isVisible).toBeFalsy(); }); + + test('sdn-preload checkbox is visible', () => { + const instance = {}; + const sdncPreload: FormControlModel = service.getSDNCControl(instance); + expect (sdncPreload.displayName).toEqual('SDN-C pre-load'); + expect (sdncPreload.value).toBeFalsy(); + }); + + test('given instance, get supp file from getSupplementaryFile ', () => { + const instance = {}; + const suppFileForInstance: FileFormControl = service.getSupplementaryFile(instance); + expect(suppFileForInstance.isVisible).toBeTruthy(); + expect(suppFileForInstance.hiddenFile.length).toBeGreaterThanOrEqual(1); + expect(suppFileForInstance.hiddenFile[0].validations[0].validatorName).toEqual("isFileTooBig"); + }); + + test('concatSupplementaryFile add SupplementaryFile control and hidden file', () => { + + //given + const instance = {}; + const controls = [service.getLegacyRegion(instance)]; + expect(controls).toHaveLength(1); + + //when + const result = service.concatSupplementaryFile(controls, instance); + + //then + expect(controls).toHaveLength(1); //original controls remain the same + + expect(result.map((control) => {return control.controlName})).toEqual([ + "legacyRegion", + "supplementaryFile", + "supplementaryFile_hidden", + "supplementaryFile_hidden_content" + ]); + }); }); diff --git a/vid-webpack-master/src/app/shared/components/genericForm/formControlsServices/basic.control.generator.ts b/vid-webpack-master/src/app/shared/components/genericForm/formControlsServices/basic.control.generator.ts index cbbff3c39..7ab64753a 100644 --- a/vid-webpack-master/src/app/shared/components/genericForm/formControlsServices/basic.control.generator.ts +++ b/vid-webpack-master/src/app/shared/components/genericForm/formControlsServices/basic.control.generator.ts @@ -1,12 +1,7 @@ import {Injectable} from "@angular/core"; import {DropdownFormControl} from "../../../models/formControlModels/dropdownFormControl.model"; import {FormGroup} from "@angular/forms"; -import { - CustomValidatorOptions, - FormControlModel, - ValidatorModel, - ValidatorOptions -} from "../../../models/formControlModels/formControl.model"; +import {CustomValidatorOptions, FormControlModel, ValidatorModel, ValidatorOptions} from "../../../models/formControlModels/formControl.model"; import {InputFormControl} from "../../../models/formControlModels/inputFormControl.model"; import {AppState} from "../../../store/reducers"; import {NgRedux} from "@angular-redux/store"; @@ -21,7 +16,11 @@ import {FormGeneralErrorsService} from "../../formGeneralErrors/formGeneralError import {Observable, of} from "rxjs"; import {NodeModel} from "../../../models/nodeModel"; import {Constants} from "../../../utils/constants"; +import {FileUnit} from "../../formControls/component/file/fileUnit.enum"; +import {CheckboxFormControl} from "../../../models/formControlModels/checkboxFormControl.model"; +export const SUPPLEMENTARY_FILE = 'supplementaryFile'; +export const SDN_C_PRE_LOAD = 'sdncPreLoad'; @Injectable() export class BasicControlGenerator { @@ -237,4 +236,71 @@ export class BasicControlGenerator { return initialInstanceName; } + concatSupplementaryFile(originalArray: FormControlModel[], vfModuleInstance): FormControlModel[] { + let suppFileInput: FileFormControl = <FileFormControl>(this.getSupplementaryFile(vfModuleInstance)); + return originalArray.concat([suppFileInput], suppFileInput.hiddenFile); + } + + getSDNCControl = (instance: any): FormControlModel => { + return new CheckboxFormControl({ + controlName: SDN_C_PRE_LOAD, + displayName: 'SDN-C pre-load', + dataTestId: 'sdncPreLoad', + value: instance ? instance.sdncPreLoad : false, + validations: [new ValidatorModel(ValidatorOptions.required, 'is required')] + }) + }; + + getSupplementaryFile(instance: any): FileFormControl { + return new FileFormControl({ + controlName: SUPPLEMENTARY_FILE, + displayName: 'Supplementary Data File (JSON format)', + dataTestId: 'SupplementaryFile', + placeHolder: 'Choose file', + selectedFile: !_.isNil(instance) ? instance.supplementaryFileName: null, + isVisible: true, + acceptedExtentions: "application/json", + hiddenFile : [new InputFormControl({ + controlName: SUPPLEMENTARY_FILE + "_hidden", + isVisible: false, + validations: [new ValidatorModel(CustomValidatorOptions.isFileTooBig, "File size exceeds 5MB.", [FileUnit.MB, 5])] + }), + new InputFormControl({ + controlName: SUPPLEMENTARY_FILE + "_hidden_content", + isVisible: false, + validations: [new ValidatorModel(CustomValidatorOptions.isValidJson, + "File is invalid, please make sure a legal JSON file is uploaded using name:value pairs.",[]), + new ValidatorModel(CustomValidatorOptions.isStringContainTags, + "File is invalid, please remove tags <>.",[])], + value: !_.isNil(instance) ? (instance.supplementaryFile_hidden_content): null, + }) + ], + onDelete : this.getOnDeleteForSupplementaryFile(), + onChange : this.getOnChangeForSupplementaryFile() + }) + }; + + private getOnDeleteForSupplementaryFile() { + return (form: FormGroup) => { + form.controls[SUPPLEMENTARY_FILE + "_hidden"].setValue(null); + form.controls[SUPPLEMENTARY_FILE + "_hidden_content"].setValue(null); + }; + } + + private getOnChangeForSupplementaryFile() { + return (files: FileList, form: FormGroup) => { + if (files.length > 0) { + const file = files.item(0); + let reader = new FileReader(); + reader.onload = function (event) { + form.controls[SUPPLEMENTARY_FILE + "_hidden_content"].setValue(reader.result); + form.controls[SUPPLEMENTARY_FILE + "_hidden"].setValue(file); + }; + reader.readAsText(file); + } else { + form.controls[SUPPLEMENTARY_FILE + "_hidden"].setValue(null); + form.controls[SUPPLEMENTARY_FILE + "_hidden_content"].setValue(null); + } + }; + } } diff --git a/vid-webpack-master/src/app/shared/components/genericForm/formControlsServices/networkGenerator/network.control.generator.spec.ts b/vid-webpack-master/src/app/shared/components/genericForm/formControlsServices/networkGenerator/network.control.generator.spec.ts index 5b64aea9b..0bb278a8e 100644 --- a/vid-webpack-master/src/app/shared/components/genericForm/formControlsServices/networkGenerator/network.control.generator.spec.ts +++ b/vid-webpack-master/src/app/shared/components/genericForm/formControlsServices/networkGenerator/network.control.generator.spec.ts @@ -7,11 +7,7 @@ import {GenericFormService} from "../../generic-form.service"; import {FormBuilder} from "@angular/forms"; import {LogService} from "../../../../utils/log/log.service"; import {FormControlNames, NetworkControlGenerator} from "./network.control.generator"; -import { - FormControlModel, - ValidatorModel, - ValidatorOptions -} from "../../../../models/formControlModels/formControl.model"; +import {FormControlModel, ValidatorModel, ValidatorOptions} from "../../../../models/formControlModels/formControl.model"; import {FeatureFlagsService} from "../../../../services/featureFlag/feature-flags.service"; class MockAppStore<T> { @@ -25,9 +21,6 @@ class MockAppStore<T> { "FLAG_FABRIC_CONFIGURATION_ASSIGNMENTS": true, "FLAG_SHOW_VERIFY_SERVICE": false, "FLAG_SERVICE_MODEL_CACHE": true, - "CREATE_INSTANCE_TEST": false, - "FLAG_SETTING_DEFAULTS_IN_DRAWING_BOARD": false, - "EMPTY_DRAWING_BOARD_TEST": false, "FLAG_ADD_MSO_TESTAPI_FIELD": true }, "type": "[FLAGS] Update" diff --git a/vid-webpack-master/src/app/shared/components/genericForm/formControlsServices/service.control.generator.spec.ts b/vid-webpack-master/src/app/shared/components/genericForm/formControlsServices/service.control.generator.spec.ts index a6a29d1df..6bcec09c4 100644 --- a/vid-webpack-master/src/app/shared/components/genericForm/formControlsServices/service.control.generator.spec.ts +++ b/vid-webpack-master/src/app/shared/components/genericForm/formControlsServices/service.control.generator.spec.ts @@ -25,9 +25,6 @@ class MockAppStore<T> { "FLAG_FABRIC_CONFIGURATION_ASSIGNMENTS": true, "FLAG_SHOW_VERIFY_SERVICE": false, "FLAG_SERVICE_MODEL_CACHE": true, - "CREATE_INSTANCE_TEST": false, - "FLAG_SETTING_DEFAULTS_IN_DRAWING_BOARD": false, - "EMPTY_DRAWING_BOARD_TEST": false, "FLAG_ADD_MSO_TESTAPI_FIELD": true }, "type": "[FLAGS] Update" diff --git a/vid-webpack-master/src/app/shared/components/genericForm/formControlsServices/vfModuleGenerator/vfModule.control.generator.spec.ts b/vid-webpack-master/src/app/shared/components/genericForm/formControlsServices/vfModuleGenerator/vfModule.control.generator.spec.ts index 351f8393c..deb1a784a 100644 --- a/vid-webpack-master/src/app/shared/components/genericForm/formControlsServices/vfModuleGenerator/vfModule.control.generator.spec.ts +++ b/vid-webpack-master/src/app/shared/components/genericForm/formControlsServices/vfModuleGenerator/vfModule.control.generator.spec.ts @@ -1,18 +1,16 @@ import {getTestBed, TestBed} from '@angular/core/testing'; import {HttpClientTestingModule, HttpTestingController} from '@angular/common/http/testing'; import {NgRedux} from '@angular-redux/store'; -import {BasicControlGenerator} from "../basic.control.generator"; +import {BasicControlGenerator, SDN_C_PRE_LOAD} from "../basic.control.generator"; import {AaiService} from "../../../../services/aaiService/aai.service"; import {GenericFormService} from "../../generic-form.service"; import {FormBuilder} from "@angular/forms"; import {LogService} from "../../../../utils/log/log.service"; -import { - FormControlModel, - ValidatorModel, - ValidatorOptions -} from "../../../../models/formControlModels/formControl.model"; +import {FormControlModel, ValidatorModel, ValidatorOptions} from "../../../../models/formControlModels/formControl.model"; import {FormControlNames, VfModuleControlGenerator} from "./vfModule.control.generator"; import {FeatureFlagsService} from "../../../../services/featureFlag/feature-flags.service"; +import {VfModuleInstance} from "../../../../models/vfModuleInstance"; +import {VfModule} from "../../../../models/vfModule"; class MockAppStore<T> { getState() { @@ -25,9 +23,6 @@ class MockAppStore<T> { "FLAG_FABRIC_CONFIGURATION_ASSIGNMENTS": true, "FLAG_SHOW_VERIFY_SERVICE": false, "FLAG_SERVICE_MODEL_CACHE": true, - "CREATE_INSTANCE_TEST": false, - "FLAG_SETTING_DEFAULTS_IN_DRAWING_BOARD": false, - "EMPTY_DRAWING_BOARD_TEST": false, "FLAG_ADD_MSO_TESTAPI_FIELD": true }, "type": "[FLAGS] Update" @@ -978,6 +973,46 @@ describe('VFModule Control Generator', () => { } }); + + const cases = [ + [true, true, true, null, true, true], + [true, true, false, null, true, false], //Scenario to check that UI field doesn't appear. + [false, true, false, "vf_vgeraldine0..VfVgeraldine..base_vflorence..module-0_vol", true, true], + [false, true, true, "vf_vgeraldine0..VfVgeraldine..base_vflorence..module-0_vol", true, true], + [false, false, true, null, false, false], + ]; + + test.each(cases)('Given Ecomp Gen Name: %p and VG Name %p , ' + + 'is A La Carte %p - expect the name value for VG to be %p , ' + + 'VG Name should be defined: %p , and should the field be visible: %p', + (ecomGenName, vGName, isALaCarte, expectedName, shouldWeVerifyDefinitionOfField, isVisible) => { + const moduleName = "vf_vgeraldine0..VfVgeraldine..base_vflorence..module-0"; + service.vfModuleModel = new VfModule(); + service.vfModuleModel.name = moduleName; + service.vfModuleModel.volumeGroupAllowed = vGName; + let vnf : Object = {isEcompGeneratedNaming: ecomGenName}; + const serviceId: string = "6e59c5de-f052-46fa-aa7e-2fca9d674c44"; + const vnfStoreKey: string = 'VF_vGeraldine 0'; + const uuidData: Object = { + modelName : moduleName, + vFModuleStoreKey : "vf_vgeraldine0..VfVgeraldine..base_vflorence..module-0vmvzo", + }; + const vfModuleModel :VfModuleInstance = service.getVfModuleInstance(serviceId, vnfStoreKey, uuidData,true); + let existingMatchingFieldInForm = buildVfModuleFormControlModel(vfModuleModel ,serviceId, vnf, isALaCarte); + if (shouldWeVerifyDefinitionOfField) { + expect(existingMatchingFieldInForm).toBeDefined(); + expect(existingMatchingFieldInForm.value).toEqual(expectedName); + expect(existingMatchingFieldInForm.isVisible).toEqual(isVisible); + } else { + expect(existingMatchingFieldInForm).toBeUndefined(); + } + }); + + let buildVfModuleFormControlModel = function(vfModuleModel :any, serviceId: string, vnf, isALaCarte) :FormControlModel { + let controls: FormControlModel[] = service.pushInstanceAndVGToForm([], vfModuleModel, serviceId, vnf, isALaCarte); + return controls.find(ctrl => ctrl.controlName === FormControlNames.VOLUME_GROUP_NAME); + }; + test('getMacroFormControls check for mandatory controls', () => { const serviceId: string = "6e59c5de-f052-46fa-aa7e-2fca9d674c44"; const vnfStoreKey: string = 'VF_vGeraldine 0'; @@ -1022,7 +1057,7 @@ describe('VFModule Control Generator', () => { FormControlNames.LEGACY_REGION, FormControlNames.TENANT_ID, FormControlNames.ROLLBACK_ON_FAILURE, - FormControlNames.SDN_C_PRE_LOAD + SDN_C_PRE_LOAD, ]; expect(controls.length).toEqual(7); @@ -1057,7 +1092,7 @@ describe('VFModule Control Generator', () => { FormControlNames.TENANT_ID, // TENANT_ID must be after LEGACY_REGION FormControlNames.LEGACY_REGION, FormControlNames.ROLLBACK_ON_FAILURE, - FormControlNames.SDN_C_PRE_LOAD + SDN_C_PRE_LOAD, ]; for(let i = 0 ; i < orderedControls.length ; i++) { @@ -1114,7 +1149,7 @@ describe('VFModule Control Generator', () => { FormControlNames.LEGACY_REGION, FormControlNames.TENANT_ID, FormControlNames.ROLLBACK_ON_FAILURE, - FormControlNames.SDN_C_PRE_LOAD + SDN_C_PRE_LOAD, ]; for(let i = 0 ; i < orderedControls.length ; i++) { diff --git a/vid-webpack-master/src/app/shared/components/genericForm/formControlsServices/vfModuleGenerator/vfModule.control.generator.ts b/vid-webpack-master/src/app/shared/components/genericForm/formControlsServices/vfModuleGenerator/vfModule.control.generator.ts index 3012c139c..8919c0419 100644 --- a/vid-webpack-master/src/app/shared/components/genericForm/formControlsServices/vfModuleGenerator/vfModule.control.generator.ts +++ b/vid-webpack-master/src/app/shared/components/genericForm/formControlsServices/vfModuleGenerator/vfModule.control.generator.ts @@ -37,8 +37,6 @@ export enum FormControlNames { LEGACY_REGION = 'legacyRegion', TENANT_ID = 'tenantId', ROLLBACK_ON_FAILURE = 'rollbackOnFailure', - SDN_C_PRE_LOAD = 'sdncPreLoad', - SUPPLEMENTARY_FILE = 'supplementaryFile' } @@ -46,7 +44,6 @@ export enum FormControlNames { export class VfModuleControlGenerator { aaiService: AaiService; vfModuleModel: VfModule; - vfModuleName : string; isUpdateMode : boolean; constructor(private genericFormService: GenericFormService, @@ -63,7 +60,6 @@ export class VfModuleControlGenerator { const vfModulesKeys = Object.keys(vfModules); for(let key of vfModulesKeys){ if(vfModules[key].uuid === vfModuleUuid){ - this.vfModuleName = key; return; } } @@ -105,15 +101,18 @@ export class VfModuleControlGenerator { let result: FormControlModel[] = []; if (!_.isNil(vfModuleModel)) { - result.push(this.getInstanceName(vfModuleInstance, serviceId, vnfModel.isEcompGeneratedNaming)); - if (this.vfModuleModel.volumeGroupAllowed) { - result.push(this.getVolumeGroupName(vfModuleInstance, serviceId, vnfStoreKey, vfModuleInstance && vfModuleInstance.volumeGroupName, vnfModel.isEcompGeneratedNaming)); - } + result = this.pushInstanceAndVGToForm(result, vfModuleInstance, serviceId, vnfModel, false); } if(this.store.getState().global.flags['FLAG_SUPPLEMENTARY_FILE']) { - let suppFileInput:FileFormControl = <FileFormControl>(this.getSupplementaryFile(vfModuleInstance)); - result.push(suppFileInput); - result = result.concat(suppFileInput.hiddenFile); + result = this._basicControlGenerator.concatSupplementaryFile(result, vfModuleInstance); + } + return result; + } + + pushInstanceAndVGToForm(result: FormControlModel[], vfModuleElement: any, serviceId: string, vnfModel: any, isALaCarte: boolean) :FormControlModel[]{ + result.push(this.getInstanceName(vfModuleElement, serviceId, vnfModel.isEcompGeneratedNaming)); + if (this.vfModuleModel.volumeGroupAllowed) { + result.push(this.getVolumeGroupData(vfModuleElement, serviceId, vnfModel.isEcompGeneratedNaming, isALaCarte)); } return result; } @@ -133,23 +132,16 @@ export class VfModuleControlGenerator { const vfModuleInstance = this.getVfModuleInstance(serviceId, vnfStoreKey, uuidData, isUpdateMode); let result: FormControlModel[] = []; - result.push(this.getInstanceName(vfModuleInstance, serviceId, vnfModel.isEcompGeneratedNaming)); - - if (this.vfModuleModel.volumeGroupAllowed) { - result.push(this.getVolumeGroupName(vfModuleInstance, serviceId, vnfStoreKey, this.vfModuleName, vnfModel.isEcompGeneratedNaming)); - } + this.pushInstanceAndVGToForm(result, vfModuleInstance, serviceId, vnfModel, true); result.push(this.getLcpRegionControl(serviceId, vfModuleInstance, result)); result.push(this._basicControlGenerator.getLegacyRegion(vfModuleInstance)); result.push(this.getTenantControl(serviceId, vfModuleInstance, result)); result.push(this.getRollbackOnFailureControl(vfModuleInstance, result)); - result.push(this.getSDNCControl(vfModuleInstance, result)); + result.push(this._basicControlGenerator.getSDNCControl(vfModuleInstance)); if(this.store.getState().global.flags['FLAG_SUPPLEMENTARY_FILE']) { - let suppFileInput:FileFormControl = <FileFormControl>(this.getSupplementaryFile(vfModuleInstance)); - result.push(suppFileInput); - result = result.concat(suppFileInput.hiddenFile); + result = this._basicControlGenerator.concatSupplementaryFile(result, vfModuleInstance); } return result; - } getInstanceName(instance: any, serviceId: string, isEcompGeneratedNaming: boolean): FormControlModel { @@ -163,7 +155,7 @@ export class VfModuleControlGenerator { return formControlModel; } - getDefaultVolumeGroupName(instance: any, vfModuleName: string, isEcompGeneratedNaming: boolean): string { + getDefaultVolumeGroupName(instance: any, isEcompGeneratedNaming: boolean): string { if ((!_.isNil(instance) && instance.volumeGroupName)) { return instance.volumeGroupName; } @@ -173,7 +165,7 @@ export class VfModuleControlGenerator { return this._basicControlGenerator.getDefaultInstanceName(instance, this.vfModuleModel) + "_vol"; } - getVolumeGroupName(instance: any, serviceId: string, vnfStoreKey: string, vfModuleName: string, isEcompGeneratedNaming: boolean): FormControlModel { + getVolumeGroupData(instance: any, serviceId: string, isEcompGeneratedNaming: boolean, isALaCarte: boolean): FormControlModel { let validations: ValidatorModel[] = [ new ValidatorModel(ValidatorOptions.pattern, 'Instance name may include only alphanumeric characters and underscore.', BasicControlGenerator.INSTANCE_NAME_REG_EX), new ValidatorModel(CustomValidatorOptions.uniqueInstanceNameValidator, 'Volume Group instance name is already in use, please pick another name', [this.store, serviceId, instance && instance.volumeGroupName]) @@ -190,8 +182,8 @@ export class VfModuleControlGenerator { validations: validations, tooltip : 'When filled, VID will create a Volume Group by this name and associate with this module.\n' + 'When empty, the module is created without a Volume Group.', - isVisible: true, - value: this.getDefaultVolumeGroupName(instance, vfModuleName, isEcompGeneratedNaming), + isVisible: this.shouldVGNameBeVisible(isEcompGeneratedNaming,isALaCarte), + value: this.getDefaultVolumeGroupName(instance, isEcompGeneratedNaming), onKeypress: (event) => { const pattern:RegExp = BasicControlGenerator.INSTANCE_NAME_REG_EX; if (pattern) { @@ -204,51 +196,13 @@ export class VfModuleControlGenerator { }); } - getSupplementaryFile(instance: any): FormControlModel { - return new FileFormControl({ - controlName: FormControlNames.SUPPLEMENTARY_FILE, - displayName: 'Supplementary Data File (JSON format)', - dataTestId: 'SupplementaryFile', - placeHolder: 'Choose file', - selectedFile: !_.isNil(instance) ? instance.supplementaryFileName: null, - isVisible: true, - acceptedExtentions: "application/json", - hiddenFile : [new InputFormControl({ - controlName: FormControlNames.SUPPLEMENTARY_FILE + "_hidden", - isVisible: false, - validations: [new ValidatorModel(CustomValidatorOptions.isFileTooBig, "File size exceeds 5MB.", [FileUnit.MB, 5])] - }), - new InputFormControl({ - controlName: FormControlNames.SUPPLEMENTARY_FILE + "_hidden_content", - isVisible: false, - validations: [new ValidatorModel(CustomValidatorOptions.isValidJson, - "File is invalid, please make sure a legal JSON file is uploaded using name:value pairs.",[]), - new ValidatorModel(CustomValidatorOptions.isStringContainTags, - "File is invalid, please remove tags <>.",[])], - value: !_.isNil(instance) ? (instance.supplementaryFile_hidden_content): null, - }) - ], - onDelete : (form : FormGroup) => { - form.controls[FormControlNames.SUPPLEMENTARY_FILE + "_hidden"].setValue(null); - form.controls[FormControlNames.SUPPLEMENTARY_FILE + "_hidden_content"].setValue(null); - }, - onChange : (files: FileList, form : FormGroup) => { - if (files.length > 0) { - const file = files.item(0); - let reader = new FileReader(); - reader.onload = function(event) { - form.controls[FormControlNames.SUPPLEMENTARY_FILE + "_hidden_content"].setValue(reader.result); - form.controls[FormControlNames.SUPPLEMENTARY_FILE + "_hidden"].setValue(file); - }; - reader.readAsText(file); - } - else { - form.controls[FormControlNames.SUPPLEMENTARY_FILE + "_hidden"].setValue(null); - form.controls[FormControlNames.SUPPLEMENTARY_FILE + "_hidden_content"].setValue(null); - } - } - }) - }; + private shouldVGNameBeVisible(isEcompGeneratedNaming: boolean, isALaCarte: boolean) { + if((!isALaCarte && !isEcompGeneratedNaming) || isALaCarte){ + return true; + } + return false; + + } getTenantControl = (serviceId: string, instance: any, controls: FormControlModel[]): DropdownFormControl => { const service = this.store.getState().service.serviceInstance[serviceId]; @@ -316,17 +270,6 @@ export class VfModuleControlGenerator { }) }; - getSDNCControl = (instance: any, controls: FormControlModel[]): CheckboxFormControl => { - return new CheckboxFormControl({ - type: FormControlType.CHECKBOX, - controlName: 'sdncPreLoad', - displayName: 'SDN-C pre-load', - dataTestId: 'sdncPreLoad', - value: instance ? instance.sdncPreLoad : false, - validations: [new ValidatorModel(ValidatorOptions.required, 'is required')] - }) - }; - getRollbackOnFailureControl = (instance: any, controls: FormControlModel[]): DropdownFormControl => { return new DropdownFormControl({ type: FormControlType.DROPDOWN, diff --git a/vid-webpack-master/src/app/shared/components/genericForm/formControlsServices/vnfGenerator/vnf.control.generator.spec.ts b/vid-webpack-master/src/app/shared/components/genericForm/formControlsServices/vnfGenerator/vnf.control.generator.spec.ts index 66afac9ad..28d49d51b 100644 --- a/vid-webpack-master/src/app/shared/components/genericForm/formControlsServices/vnfGenerator/vnf.control.generator.spec.ts +++ b/vid-webpack-master/src/app/shared/components/genericForm/formControlsServices/vnfGenerator/vnf.control.generator.spec.ts @@ -6,16 +6,13 @@ import {BasicControlGenerator} from "../basic.control.generator"; import {AaiService} from "../../../../services/aaiService/aai.service"; import {GenericFormService} from "../../generic-form.service"; import {FormBuilder} from "@angular/forms"; -import { - FormControlModel, - ValidatorModel, - ValidatorOptions -} from "../../../../models/formControlModels/formControl.model"; +import {FormControlModel, ValidatorModel, ValidatorOptions} from "../../../../models/formControlModels/formControl.model"; import {LogService} from "../../../../utils/log/log.service"; import {VnfControlGenerator} from "./vnf.control.generator"; import {Observable} from "rxjs"; import {SelectOption} from "../../../../models/selectOption"; import {FeatureFlagsService} from "../../../../services/featureFlag/feature-flags.service"; +import {FormControlType} from "../../../../models/formControlModels/formControlTypes.enum"; class MockAppStore<T> { getState(){ @@ -28,9 +25,6 @@ class MockAppStore<T> { "FLAG_FABRIC_CONFIGURATION_ASSIGNMENTS": true, "FLAG_SHOW_VERIFY_SERVICE": false, "FLAG_SERVICE_MODEL_CACHE": true, - "CREATE_INSTANCE_TEST": false, - "FLAG_SETTING_DEFAULTS_IN_DRAWING_BOARD": false, - "EMPTY_DRAWING_BOARD_TEST": false, "FLAG_ADD_MSO_TESTAPI_FIELD": true }, "type": "[FLAGS] Update" @@ -941,6 +935,18 @@ describe('VNF Control Generator', () => { })().then(done).catch(done.fail)); + test('should generate platform multi select control', ()=>{ + const control = service.getPlatformMultiselectControl(null, [],false); + expect(control.type).toEqual(FormControlType.MULTI_SELECT); + expect(control.controlName).toEqual('platformName'); + expect(control.displayName).toEqual('Platform'); + expect(control.dataTestId).toEqual('multi-selectPlatform'); + expect(control.selectedFieldName).toEqual('name'); + expect(control.value).toEqual(''); + expect(control.onChange).toBeDefined(); + expect(control.convertOriginalDataToArray).toBeDefined(); + }); + test('getMacroFormControls check for mandatory controls', () => { const serviceId : string = "6e59c5de-f052-46fa-aa7e-2fca9d674c44"; const vnfName : string = "VF_vGeraldine 0"; diff --git a/vid-webpack-master/src/app/shared/components/genericForm/formControlsServices/vnfGenerator/vnf.control.generator.ts b/vid-webpack-master/src/app/shared/components/genericForm/formControlsServices/vnfGenerator/vnf.control.generator.ts index 7760ba8ad..ff0a525b5 100644 --- a/vid-webpack-master/src/app/shared/components/genericForm/formControlsServices/vnfGenerator/vnf.control.generator.ts +++ b/vid-webpack-master/src/app/shared/components/genericForm/formControlsServices/vnfGenerator/vnf.control.generator.ts @@ -15,11 +15,12 @@ import {AppState} from "../../../../store/reducers"; import {FormGroup} from "@angular/forms"; import {DropdownFormControl} from "../../../../models/formControlModels/dropdownFormControl.model"; import {FormControlType} from "../../../../models/formControlModels/formControlTypes.enum"; -import {InputFormControl} from "../../../../models/formControlModels/inputFormControl.model"; import {Observable, of} from "rxjs"; import {SelectOption} from "../../../../models/selectOption"; import * as _ from 'lodash'; import {Constants} from "../../../../utils/constants"; +import {MultiselectFormControl} from "../../../../models/formControlModels/multiselectFormControl.model"; +import {MultiSelectItem} from "../../../formControls/component/multiselect/multiselect.model"; export enum FormControlNames { INSTANCE_NAME = 'instanceName', @@ -76,6 +77,7 @@ export class VnfControlGenerator { const vnfInstance = this.getVnfInstance(serviceId, vnfStoreKey); const vnfModel = new VNFModel(this.store.getState().service.serviceHierarchy[serviceId].vnfs[vnfName]); let result: FormControlModel[] = []; + const flags = this.store.getState().global.flags; if (!_.isNil(vnfModel)) { result.push(this.getInstanceName(vnfInstance, serviceId, vnfName, vnfModel.isEcompGeneratedNaming)); @@ -83,7 +85,7 @@ export class VnfControlGenerator { result.push(this.getLcpRegionControl(serviceId, vnfInstance, result)); result.push(this._basicControlGenerator.getLegacyRegion(vnfInstance)); result.push(this.getTenantControl(serviceId, vnfInstance, result)); - result.push(this.getPlatformControl(vnfInstance, result)); + result.push(this.getPlatformMultiselectControl(vnfInstance, result, flags['FLAG_2002_VNF_PLATFORM_MULTI_SELECT'])); result.push(this.getLineOfBusinessControl(vnfInstance, result)); } return result; @@ -101,12 +103,13 @@ export class VnfControlGenerator { const vnfModel = new VNFModel(this.store.getState().service.serviceHierarchy[serviceId].vnfs[vnfName]); if (!_.isNil(vnfModel)) { + const flags = this.store.getState().global.flags; result.push(this.getInstanceName(vnfInstance, serviceId, vnfName, vnfModel.isEcompGeneratedNaming)); result.push(this._basicControlGenerator.getProductFamilyControl(vnfInstance, result, false)); result.push(this.getLcpRegionControl(serviceId, vnfInstance, result)); result.push(this._basicControlGenerator.getLegacyRegion(vnfInstance)); result.push(this.getTenantControl(serviceId, vnfInstance, result)); - result.push(this.getPlatformControl(vnfInstance, result)); + result.push(this.getPlatformMultiselectControl(vnfInstance, result, flags['FLAG_2002_VNF_PLATFORM_MULTI_SELECT'])); result.push(this.getLineOfBusinessControl(vnfInstance, result)); result.push(this.getRollbackOnFailureControl(vnfInstance, result)); } @@ -139,20 +142,34 @@ export class VnfControlGenerator { }) }; - getPlatformControl = (instance: any, controls: FormControlModel[]): DropdownFormControl => { - return new DropdownFormControl({ - type: FormControlType.DROPDOWN, + + + getPlatformMultiselectControl = (instance: any, controls: FormControlModel[], isMultiSelected: boolean) : MultiselectFormControl => { + return new MultiselectFormControl({ + type: FormControlType.MULTI_SELECT , controlName: 'platformName', displayName: 'Platform', - dataTestId: 'platform', + dataTestId: 'multi-selectPlatform', + selectedFieldName : 'name' , + ngValue : 'name', placeHolder: 'Select Platform', isDisabled: false, name: "platform", - value: instance ? instance.platformName : null, + value: instance ? instance.platformName : '', + limitSelection : isMultiSelected ? 1000 : 1, validations: [new ValidatorModel(ValidatorOptions.required, 'is required')], onInitSelectedField: ['platformList'], - onInit: this._basicControlGenerator.getSubscribeInitResult.bind(null, this._aaiService.getCategoryParameters) - }) + onInit: this._basicControlGenerator.getSubscribeInitResult.bind(null, this._aaiService.getCategoryParameters), + onChange : (param: MultiSelectItem[], form: FormGroup) => { + form.controls['platformName'].setValue(param.map((multiSelectItem: MultiSelectItem)=>{ + return multiSelectItem.itemName + }).join(',')); + }, + convertOriginalDataToArray : (value?: string) => { + if(_.isNil(value)) return []; + return value.split(','); + } + }); }; getTenantControl = (serviceId: string, instance: any, controls: FormControlModel[]): DropdownFormControl => { diff --git a/vid-webpack-master/src/app/shared/components/genericForm/formControlsServices/vnfGroupGenerator/vnfGroup.control.generator.spec.ts b/vid-webpack-master/src/app/shared/components/genericForm/formControlsServices/vnfGroupGenerator/vnfGroup.control.generator.spec.ts index 71d661191..81cfd9614 100644 --- a/vid-webpack-master/src/app/shared/components/genericForm/formControlsServices/vnfGroupGenerator/vnfGroup.control.generator.spec.ts +++ b/vid-webpack-master/src/app/shared/components/genericForm/formControlsServices/vnfGroupGenerator/vnfGroup.control.generator.spec.ts @@ -6,11 +6,7 @@ import {BasicControlGenerator} from "../basic.control.generator"; import {AaiService} from "../../../../services/aaiService/aai.service"; import {GenericFormService} from "../../generic-form.service"; import {FormBuilder} from "@angular/forms"; -import { - FormControlModel, - ValidatorModel, - ValidatorOptions -} from "../../../../models/formControlModels/formControl.model"; +import {FormControlModel, ValidatorModel, ValidatorOptions} from "../../../../models/formControlModels/formControl.model"; import {LogService} from "../../../../utils/log/log.service"; import {VnfGroupControlGenerator} from "./vnfGroup.control.generator"; import {Observable} from "rxjs"; @@ -25,19 +21,14 @@ class MockAppStore<T> { "type": "UPDATE_DRAWING_BOARD_STATUS", "drawingBoardStatus": "CREATE", "flags": { - "CREATE_INSTANCE_TEST": false, - "EMPTY_DRAWING_BOARD_TEST": false, "FLAG_NETWORK_TO_ASYNC_INSTANTIATION": false, "FLAG_ADD_MSO_TESTAPI_FIELD": true, "FLAG_SERVICE_MODEL_CACHE": false, "FLAG_SHOW_ASSIGNMENTS": true, "FLAG_FABRIC_CONFIGURATION_ASSIGNMENTS": true, - "FLAG_DEFAULT_VNF": true, - "FLAG_SETTING_DEFAULTS_IN_DRAWING_BOARD": true, "FLAG_A_LA_CARTE_AUDIT_INFO": true, "FLAG_1810_CR_ADD_CLOUD_OWNER_TO_MSO_REQUEST": true, "FLAG_PRESENT_PROVIDER_NETWORKS_ASSOCIATIONS": true, - "FLAG_1810_CR_SOFT_DELETE_ALACARTE_VF_MODULE": true, "FLAG_1902_NEW_VIEW_EDIT": true } }, diff --git a/vid-webpack-master/src/app/shared/components/genericForm/generic-form.component.html b/vid-webpack-master/src/app/shared/components/genericForm/generic-form.component.html index d4c5118b3..edf86823c 100644 --- a/vid-webpack-master/src/app/shared/components/genericForm/generic-form.component.html +++ b/vid-webpack-master/src/app/shared/components/genericForm/generic-form.component.html @@ -6,6 +6,11 @@ <checkbox-form-control *ngSwitchCase="'CHECKBOX'" [data]="formControl" [form]="dynamicFormGroup" ></checkbox-form-control> <dropdown-form-control *ngSwitchCase="'DROPDOWN'" [data]="formControl" [form]="dynamicFormGroup" ></dropdown-form-control> <file-form-control *ngSwitchCase="'FILE'" [data]="formControl" [form]="dynamicFormGroup"></file-form-control> + <multiselect-form-control *ngSwitchCase="'MULTI_SELECT'" + [data]="formControl" + [form]="dynamicFormGroup" + [multiselectOptions]="formControl?.options$" + [selectedItems]="formControl.value"></multiselect-form-control> </div> <div *ngIf="dynamicFormGroup != null && formControl != null && dynamicFormGroup.controls[formControl.controlName]?.errors"> <div *ngFor="let validatorModel of formControl?.validations"> diff --git a/vid-webpack-master/src/app/shared/components/genericFormPopup/generic-form-popup.component.html b/vid-webpack-master/src/app/shared/components/genericFormPopup/generic-form-popup.component.html index 57064f658..f9a11eab1 100644 --- a/vid-webpack-master/src/app/shared/components/genericFormPopup/generic-form-popup.component.html +++ b/vid-webpack-master/src/app/shared/components/genericFormPopup/generic-form-popup.component.html @@ -3,14 +3,13 @@ <div class="modal-header"> <button type="button" class="close" - (click)="formPopupDetails?.onCancel(formPopupDetails.that,dynamicForm)" >× + (click)="formPopupDetails?.onCancel(formPopupDetails.that,dynamicForm)">× </button> <span [attr.data-tests-id]="'create-modal-title'" class="modal-title">{{formPopupDetails?.title}} </span> </div> <div class="modal-body popup-content"> - <div class="header-left"> <div>MODEL: <span>"{{formPopupDetails?.leftSubTitle}}"</span></div> </div> @@ -36,12 +35,14 @@ <model-information [modelInformationItems]="formPopupDetails?.modelInformationItems"></model-information> </div> - <div class="instance-form"> + <div class="instance-form"> <div style="position: relative;height: 100%;overflow: auto;"> - <label id="notification-area" *ngIf="shouldShowNotification() == true" style="color: #959595;font-size: 12px;left: 30px;margin-left: 30px;">Data entered will apply to all service instances</label> - <generic-form [formControls]="formPopupDetails?.formControlList" - [dynamicInputs]="formPopupDetails?.dynamicInputsControlList" - (onFormChanged)="dynamicForm = $event" ></generic-form> + <label id="notification-area" *ngIf="shouldShowNotification() == true" + style="color: #959595;font-size: 12px;left: 30px;margin-left: 30px;">Data entered will apply to all + service instances</label> + <generic-form [formControls]="formPopupDetails?.formControlList" + [dynamicInputs]="formPopupDetails?.dynamicInputsControlList" + (onFormChanged)="dynamicForm = $event"></generic-form> </div> </div> @@ -54,6 +55,19 @@ </div> <div class="col-md-6" style="padding: 15px;padding-right: 35px;"> <button + *ngIf="showTemplateBtn" + [attr.data-tests-id]="'templateButton'" + type="button" class="btn btn-success submit" + (click)="openTemplateModal()" + ><span>Template</span></button> + <button + *ngIf="isShowPreviousInstantiationBtn" + [attr.data-tests-id]="'ShowPreviousInstancesButton'" + type="button" class="btn btn-success submit" + (click)="formPopupDetails.onOtherAction(formPopupDetails.that, dynamicForm)"> + <span>Previous Instantiation</span> + </button> + <button [attr.data-tests-id]="'cancelButton'" type="button" class="btn btn-default cancel" (click)="formPopupDetails.onCancel(formPopupDetails.that, dynamicForm)"><span>Cancel</span></button> diff --git a/vid-webpack-master/src/app/shared/components/genericFormPopup/generic-form-popup.component.scss b/vid-webpack-master/src/app/shared/components/genericFormPopup/generic-form-popup.component.scss index 5057b44a5..18416dae6 100644 --- a/vid-webpack-master/src/app/shared/components/genericFormPopup/generic-form-popup.component.scss +++ b/vid-webpack-master/src/app/shared/components/genericFormPopup/generic-form-popup.component.scss @@ -134,7 +134,7 @@ $grid-border: 1px #d2d2d2 solid; } .submit { - width: 120px; + min-width: 120px; height: 36px; background: #009fdb; border-radius: 2px; diff --git a/vid-webpack-master/src/app/shared/components/genericFormPopup/generic-form-popup.component.ts b/vid-webpack-master/src/app/shared/components/genericFormPopup/generic-form-popup.component.ts index 8a95d108f..3939e44ff 100644 --- a/vid-webpack-master/src/app/shared/components/genericFormPopup/generic-form-popup.component.ts +++ b/vid-webpack-master/src/app/shared/components/genericFormPopup/generic-form-popup.component.ts @@ -13,55 +13,62 @@ import {AaiService} from "../../services/aaiService/aai.service"; import {GenericFormPopupService} from "./generic-form-popup.service"; import {FormControlModel} from "../../models/formControlModels/formControl.model"; import {FormGeneralErrorsService} from "../formGeneralErrors/formGeneralErrors.service"; +import {FeatureFlagsService, Features} from "../../services/featureFlag/feature-flags.service"; +import {InstantiationTemplatesModalComponent} from "./instantiationTemplatesModal/instantiation.templates.modal.component"; export interface PopupModel { - type : PopupType; - uuidData : UUIDData; - node : ITreeNode; - isUpdateMode : boolean; + type: PopupType; + uuidData: UUIDData; + node: ITreeNode; + isUpdateMode: boolean; } -export enum PopupType{ +export enum PopupType { SERVICE = 'service', VNF = 'vnf', NETWORK = 'network', VF_MODULE = 'vf_module', + VF_MODULE_UPGRADE = 'vf_module_upgrade', VNF_GROUP = 'vnf_group' } @Component({ - selector : 'generic-form-popup', - templateUrl : 'generic-form-popup.component.html', - styleUrls : ['generic-form-popup.component.scss'] + selector: 'generic-form-popup', + templateUrl: 'generic-form-popup.component.html', + styleUrls: ['generic-form-popup.component.scss'] }) -export class GenericFormPopupComponent extends DialogComponent<PopupModel, boolean> implements OnInit, OnDestroy{ - formPopupDetails : FormPopupDetails = null; - dynamicForm : FormGroup; - type : PopupType; - uuidData : UUIDData; - isUpdateMode : boolean; - node : ITreeNode = null; - hasGeneralApiError : boolean = false; +export class GenericFormPopupComponent extends DialogComponent<PopupModel, boolean> implements OnInit, OnDestroy { + formPopupDetails: FormPopupDetails = null; + dynamicForm: FormGroup; + type: PopupType; + uuidData: UUIDData; + showTemplateBtn: boolean = false; + isShowPreviousInstantiationBtn: boolean = false; + isUpdateMode: boolean; + node: ITreeNode = null; + hasGeneralApiError: boolean = false; parentElementClassName = 'content'; errorMsg = 'Page contains errors. Please see details next to the relevant fields.'; servicesQty = 1; quantityOptions = _.range(1, 51) - constructor(dialogService: DialogService , - private _iframeService : IframeService, + + constructor(dialogService: DialogService, + private _iframeService: IframeService, private _store: NgRedux<AppState>, - private _servicePopupService : ServicePopupService, - private _activatedRoute : ActivatedRoute, - private _aaiService : AaiService, + private _servicePopupService: ServicePopupService, + private _activatedRoute: ActivatedRoute, + private _aaiService: AaiService, + private _dialogService: DialogService, private _route: ActivatedRoute, - private _genericFormPopupService : GenericFormPopupService){ + private _genericFormPopupService: GenericFormPopupService) { super(dialogService); } - closeDialog(that) : void{ + closeDialog(that): void { this._iframeService.removeClassCloseModal(that.parentElementClassName); this.dialogService.removeDialog(this); setTimeout(() => { @@ -69,7 +76,7 @@ export class GenericFormPopupComponent extends DialogComponent<PopupModel, boole }, 15); } - shouldShowNotification() : boolean { + shouldShowNotification(): boolean { return this.formPopupDetails && this.formPopupDetails.UUIDData['bulkSize'] > 1 } @@ -78,38 +85,17 @@ export class GenericFormPopupComponent extends DialogComponent<PopupModel, boole .queryParams .subscribe(params => { console.log('changed'); - if(params['serviceModelId'] && params['isCreate']=="true"){ - this._genericFormPopupService.initReduxOnCreateNewService().then((serviceModelId : string)=>{ - this.uuidData = <any>{ - bulkSize : 1, - isMacro : this._store.getState().service.serviceHierarchy[serviceModelId].service.vidNotions.instantiationType === 'Macro', - type : PopupType.SERVICE, - serviceId: serviceModelId, - popupService: this._servicePopupService, - }; - - this.uuidData.popupService.closeDialogEvent.subscribe((that)=>{ - this.closeDialog(that); - }); - - this.formPopupDetails = this.uuidData.popupService.getGenericFormPopupDetails( - this.uuidData['serviceId'], - null, - null, - this.node, - this.uuidData, - false - ); - }); + if (params['serviceModelId'] && params['isCreate'] == "true") { + this.onInitForCreateNewServicePopup(); } }); - FormGeneralErrorsService.checkForErrorTrigger.subscribe(()=>{ + FormGeneralErrorsService.checkForErrorTrigger.subscribe(() => { this.hasSomeError(this.formPopupDetails, this.dynamicForm); }); - - if(!_.isNil(this.uuidData)){ - this.uuidData.popupService.closeDialogEvent.subscribe((that)=>{ + + if (!_.isNil(this.uuidData)) { + this.uuidData.popupService.closeDialogEvent.subscribe((that) => { this.closeDialog(that); }); @@ -118,28 +104,62 @@ export class GenericFormPopupComponent extends DialogComponent<PopupModel, boole } } - hasSomeError(formPopupDetails : FormPopupDetails, form : FormGroup) : boolean{ - if(_.isNil(formPopupDetails)) return false; + private onInitForCreateNewServicePopup() { + this._genericFormPopupService.initReduxOnCreateNewService().then((serviceModelId: string) => { + this.uuidData = <any>{ + bulkSize: 1, + isMacro: this._store.getState().service.serviceHierarchy[serviceModelId].service.vidNotions.instantiationType === 'Macro', + type: PopupType.SERVICE, + serviceId: serviceModelId, + popupService: this._servicePopupService, + }; + this.showTemplateBtn = !!this._store.getState().global.flags["FLAG_2004_INSTANTIATION_TEMPLATES_POPUP"]; + + this.isShowPreviousInstantiationBtn = !!this._store.getState().global.flags["FLAG_2004_TEMP_BUTTON_TO_INSTANTIATION_STATUS_FILTER"]; + + this.uuidData.popupService.closeDialogEvent.subscribe((that) => { + this.closeDialog(that); + }); + + this.formPopupDetails = this.uuidData.popupService.getGenericFormPopupDetails( + this.uuidData['serviceId'], + null, + null, + this.node, + this.uuidData, + false + ); + }); + } + + hasSomeError(formPopupDetails: FormPopupDetails, form: FormGroup): boolean { + if (_.isNil(formPopupDetails)) return false; else { - for(let controlName in form.controls){ - if(form.controls[controlName].errors){ + for (let controlName in form.controls) { + if (form.controls[controlName].errors) { let error: string[] = Object.keys(form.controls[controlName].errors); - if(error.length === 1 && error[0] === 'required'){ + if (error.length === 1 && error[0] === 'required') { continue; - }else if(Object.keys(form.controls[controlName].errors).length > 0 ){ + } else if (Object.keys(form.controls[controlName].errors).length > 0) { return true; } } } } - return formPopupDetails.formControlList.filter((item : FormControlModel) => item.type === 'DROPDOWN' && item['hasEmptyOptions'] && item.isRequired()).length > 0 + return formPopupDetails.formControlList.filter((item: FormControlModel) => item.type === 'DROPDOWN' && item['hasEmptyOptions'] && item.isRequired()).length > 0 } + + + openTemplateModal = (): void => { + this._dialogService.addDialog(InstantiationTemplatesModalComponent, {}); + } + } -export class UUIDData extends Object{ - type : string; - popupService : any; +export class UUIDData extends Object { + type: string; + popupService: any; } diff --git a/vid-webpack-master/src/app/shared/components/genericFormPopup/generic-form-popup.service.spec.ts b/vid-webpack-master/src/app/shared/components/genericFormPopup/generic-form-popup.service.spec.ts index fa77bed5f..63c980cd5 100644 --- a/vid-webpack-master/src/app/shared/components/genericFormPopup/generic-form-popup.service.spec.ts +++ b/vid-webpack-master/src/app/shared/components/genericFormPopup/generic-form-popup.service.spec.ts @@ -20,6 +20,7 @@ import {NetworkControlGenerator} from "../genericForm/formControlsServices/netwo import {VfModulePopuopService} from "./genericFormServices/vfModule/vfModule.popuop.service"; import {VfModuleControlGenerator} from "../genericForm/formControlsServices/vfModuleGenerator/vfModule.control.generator"; import {FeatureFlagsService} from "../../services/featureFlag/feature-flags.service"; +import {VfModuleUpgradePopupService} from "./genericFormServices/vfModuleUpgrade/vfModule.upgrade.popuop.service"; class MockAppStore<T>{ getState() { @@ -32,9 +33,6 @@ class MockAppStore<T>{ "FLAG_FABRIC_CONFIGURATION_ASSIGNMENTS": true, "FLAG_SHOW_VERIFY_SERVICE": false, "FLAG_SERVICE_MODEL_CACHE": true, - "CREATE_INSTANCE_TEST": false, - "FLAG_SETTING_DEFAULTS_IN_DRAWING_BOARD": false, - "EMPTY_DRAWING_BOARD_TEST": false, "FLAG_ADD_MSO_TESTAPI_FIELD": true }, "type": "[FLAGS] Update" @@ -970,6 +968,7 @@ describe('Generic Form popup Service', () => { NetworkPopupService, NetworkControlGenerator, VfModulePopuopService, + VfModuleUpgradePopupService, VfModuleControlGenerator, {provide:FeatureFlagsService, useClass: MockFeatureFlagsService}, {provide: ActivatedRoute, useClass: ActivatedRouteMock}, diff --git a/vid-webpack-master/src/app/shared/components/genericFormPopup/genericFormServices/basic.popup.service.spec.ts b/vid-webpack-master/src/app/shared/components/genericFormPopup/genericFormServices/basic.popup.service.spec.ts index 54af063d8..a3c83263c 100644 --- a/vid-webpack-master/src/app/shared/components/genericFormPopup/genericFormServices/basic.popup.service.spec.ts +++ b/vid-webpack-master/src/app/shared/components/genericFormPopup/genericFormServices/basic.popup.service.spec.ts @@ -33,10 +33,7 @@ class MockReduxStore<T> { "FLAG_SHOW_VERIFY_SERVICE": false, "FLAG_SERVICE_MODEL_CACHE": true, "FLAG_ADVANCED_PORTS_FILTER": true, - "CREATE_INSTANCE_TEST": false, - "FLAG_SETTING_DEFAULTS_IN_DRAWING_BOARD": false, "FLAG_REGION_ID_FROM_REMOTE": true, - "EMPTY_DRAWING_BOARD_TEST": false, "FLAG_ADD_MSO_TESTAPI_FIELD": true }, "type": "[FLAGS] Update" diff --git a/vid-webpack-master/src/app/shared/components/genericFormPopup/genericFormServices/basic.popup.service.ts b/vid-webpack-master/src/app/shared/components/genericFormPopup/genericFormServices/basic.popup.service.ts index 5d4d16567..1d44a9e5e 100644 --- a/vid-webpack-master/src/app/shared/components/genericFormPopup/genericFormServices/basic.popup.service.ts +++ b/vid-webpack-master/src/app/shared/components/genericFormPopup/genericFormServices/basic.popup.service.ts @@ -10,6 +10,7 @@ import * as _ from 'lodash'; import {VfModule} from "../../../models/vfModule"; import {VNFModel} from "../../../models/vnfModel"; import {VnfGroupModel} from "../../../models/vnfGroupModel"; +import {FeatureFlagsService} from "../../../services/featureFlag/feature-flags.service"; @Injectable() export class BasicPopupService { @@ -29,17 +30,18 @@ export class BasicPopupService { } getModelFromResponse(result: any, modelType: string, modelName: string) { + let flags = FeatureFlagsService.getAllFlags(this._store); let rawModel = result[modelType][modelName]; if (!rawModel) return; switch (modelType){ case 'vnfs' : { - return new VNFModel(rawModel); + return new VNFModel(rawModel, flags); } case 'vfModules' : { - return new VfModule(rawModel); + return new VfModule(rawModel, flags); } case 'networks' : { - return new NetworkModel(rawModel); + return new NetworkModel(rawModel, flags); } case 'vnfGroups' : { return new VnfGroupModel(rawModel); diff --git a/vid-webpack-master/src/app/shared/components/genericFormPopup/genericFormServices/network/network.popup.service.spec.ts b/vid-webpack-master/src/app/shared/components/genericFormPopup/genericFormServices/network/network.popup.service.spec.ts index eb094abfb..67c371212 100644 --- a/vid-webpack-master/src/app/shared/components/genericFormPopup/genericFormServices/network/network.popup.service.spec.ts +++ b/vid-webpack-master/src/app/shared/components/genericFormPopup/genericFormServices/network/network.popup.service.spec.ts @@ -30,10 +30,7 @@ class MockReduxStore<T> { "FLAG_SHOW_VERIFY_SERVICE": false, "FLAG_SERVICE_MODEL_CACHE": true, "FLAG_ADVANCED_PORTS_FILTER": true, - "CREATE_INSTANCE_TEST": false, - "FLAG_SETTING_DEFAULTS_IN_DRAWING_BOARD": false, "FLAG_REGION_ID_FROM_REMOTE": true, - "EMPTY_DRAWING_BOARD_TEST": false, "FLAG_ADD_MSO_TESTAPI_FIELD": true }, "type": "[FLAGS] Update" diff --git a/vid-webpack-master/src/app/shared/components/genericFormPopup/genericFormServices/service/service.popup.service.spec.ts b/vid-webpack-master/src/app/shared/components/genericFormPopup/genericFormServices/service/service.popup.service.spec.ts index 5b5acd33b..ebea695f7 100644 --- a/vid-webpack-master/src/app/shared/components/genericFormPopup/genericFormServices/service/service.popup.service.spec.ts +++ b/vid-webpack-master/src/app/shared/components/genericFormPopup/genericFormServices/service/service.popup.service.spec.ts @@ -31,10 +31,7 @@ class MockReduxStore<T> { "FLAG_SHOW_VERIFY_SERVICE": false, "FLAG_SERVICE_MODEL_CACHE": true, "FLAG_ADVANCED_PORTS_FILTER": true, - "CREATE_INSTANCE_TEST": false, - "FLAG_SETTING_DEFAULTS_IN_DRAWING_BOARD": false, "FLAG_REGION_ID_FROM_REMOTE": true, - "EMPTY_DRAWING_BOARD_TEST": false, "FLAG_ADD_MSO_TESTAPI_FIELD": true }, "type": "[FLAGS] Update" @@ -2143,6 +2140,33 @@ describe('Service popup service', () => { expect(service.closeDialogEvent.next).toHaveBeenCalledWith(that); }); + test('showPreviousInstantiations should trigger postMessage', () => { + let that = <any>{ + parentElementClassName: 'content', + _iframeService: iframeService, + resetPopupData : () =>{ }, + serviceModel:{ + uuid:'1111' + } + + }; + + let expectedMessage= { + eventId: 'showPreviousInstantiations', + data: { + serviceModelId: that.serviceModel.uuid + } + }; + + jest.spyOn(window.parent, 'postMessage'); + + service.showPreviousInstantiations(that, fb.group({})); + + expect( window.parent.postMessage).toHaveBeenCalledWith(expectedMessage,"*") + + }); + + test('getDynamicInputs should return list of controls' ,() => { const result: FormControlModel[] = service.getDynamicInputs('6b528779-44a3-4472-bdff-9cd15ec93450'); diff --git a/vid-webpack-master/src/app/shared/components/genericFormPopup/genericFormServices/service/service.popup.service.ts b/vid-webpack-master/src/app/shared/components/genericFormPopup/genericFormServices/service/service.popup.service.ts index 7694e6314..3e7e8c1e5 100644 --- a/vid-webpack-master/src/app/shared/components/genericFormPopup/genericFormServices/service/service.popup.service.ts +++ b/vid-webpack-master/src/app/shared/components/genericFormPopup/genericFormServices/service/service.popup.service.ts @@ -20,6 +20,7 @@ import {ModelInfo} from "../../../../models/modelInfo"; import {FormControlModel} from "../../../../models/formControlModels/formControl.model"; import {createServiceInstance, updateServiceInstance} from "../../../../storeUtil/utils/service/service.actions"; import * as _ from 'lodash'; +import {Utils} from "../../../../utils/utils"; @Injectable() export class ServicePopupService implements GenericPopupInterface { @@ -58,7 +59,12 @@ export class ServicePopupService implements GenericPopupInterface { this.getDynamicInputs(serviceId), this.modelInformations, (that, form: FormGroup) => {that.onSubmit(that, form);}, - (that: any, form: FormGroup) => {that.onCancel(that, form); } + (that: any, form: FormGroup) => { + that.onCancel(that, form); + }, + (that: any, form: FormGroup) => { + that.showPreviousInstantiations(that, form); + } ); } @@ -122,13 +128,24 @@ export class ServicePopupService implements GenericPopupInterface { onSubmit(that, form: FormGroup, ...args): void { form = that.updateExtraValues(that, form); that.storeServiceInstance(form.value, args[0], [], new ModelInfo(that.serviceModel), that.serviceModel); - window.parent.postMessage( { - eventId: 'submitIframe', + const eventId = 'submitIframe'; + this.postMessageToWindowParent(eventId, that.serviceModel.uuid); + this.onCancel(that, form); + } + + showPreviousInstantiations(that, form: FormGroup,): void { + const eventId = 'showPreviousInstantiations'; + this.postMessageToWindowParent(eventId, that.serviceModel.uuid); + this.onCancel(that, form); + } + + private postMessageToWindowParent(eventId: string, serviceModelId:string) { + window.parent.postMessage({ + eventId: eventId, data: { - serviceModelId: that.serviceModel.uuid + serviceModelId } }, "*"); - this.onCancel(that, form); } updateExtraValues = (that, form) : any => { @@ -163,13 +180,13 @@ export class ServicePopupService implements GenericPopupInterface { this._store.dispatch(updateServiceInstance(formValues, serviceModel.modelVersionId)); } - if (this._store.getState().global.flags['FLAG_SETTING_DEFAULTS_IN_DRAWING_BOARD'] && isCreateMode) { + if (isCreateMode) { this._defaultDataGeneratorService.updateReduxOnFirstSet(serviceModel.modelVersionId, formValues); } }; setIsALaCarte = (formValues: any, instantiationType) => { - formValues.isALaCarte = instantiationType === 'ALaCarte'; + formValues.isALaCarte = Utils.isALaCarte(instantiationType); }; setTestApi = (formValues: any) =>{ diff --git a/vid-webpack-master/src/app/shared/components/genericFormPopup/genericFormServices/vfModule/vfModule.popuop.service.spec.ts b/vid-webpack-master/src/app/shared/components/genericFormPopup/genericFormServices/vfModule/vfModule.popuop.service.spec.ts index 5b27b6e29..6c487102f 100644 --- a/vid-webpack-master/src/app/shared/components/genericFormPopup/genericFormServices/vfModule/vfModule.popuop.service.spec.ts +++ b/vid-webpack-master/src/app/shared/components/genericFormPopup/genericFormServices/vfModule/vfModule.popuop.service.spec.ts @@ -30,10 +30,7 @@ class MockReduxStore<T> { "FLAG_SHOW_VERIFY_SERVICE": false, "FLAG_SERVICE_MODEL_CACHE": true, "FLAG_ADVANCED_PORTS_FILTER": true, - "CREATE_INSTANCE_TEST": false, - "FLAG_SETTING_DEFAULTS_IN_DRAWING_BOARD": false, "FLAG_REGION_ID_FROM_REMOTE": true, - "EMPTY_DRAWING_BOARD_TEST": false, "FLAG_ADD_MSO_TESTAPI_FIELD": true }, "type": "[FLAGS] Update" diff --git a/vid-webpack-master/src/app/shared/components/genericFormPopup/genericFormServices/vfModule/vfModule.popuop.service.ts b/vid-webpack-master/src/app/shared/components/genericFormPopup/genericFormServices/vfModule/vfModule.popuop.service.ts index c7a71efc0..20336e5eb 100644 --- a/vid-webpack-master/src/app/shared/components/genericFormPopup/genericFormServices/vfModule/vfModule.popuop.service.ts +++ b/vid-webpack-master/src/app/shared/components/genericFormPopup/genericFormServices/vfModule/vfModule.popuop.service.ts @@ -21,8 +21,7 @@ import {FormControlModel} from "../../../../models/formControlModels/formControl import * as _ from 'lodash'; import {createVFModuleInstance, updateVFModuleInstance} from "../../../../storeUtil/utils/vfModule/vfModule.actions"; -@Injectable() -export class VfModulePopuopService implements GenericPopupInterface { +export abstract class VfModulePopupServiceBase { dynamicInputs: any; instance: any; model: any; @@ -31,16 +30,27 @@ export class VfModulePopuopService implements GenericPopupInterface { uuidData: Object; closeDialogEvent: Subject<any> = new Subject<any>(); isUpdateMode: boolean; + storeVFModule = (that, formValues: any): void => { + formValues.modelInfo = new ModelInfo(that.model); + formValues.uuid = formValues.modelInfo.uuid; + formValues.isMissingData = false; + const vnf = that._store.getState().service.serviceInstance[that.uuidData.serviceId].vnfs[that.uuidData.vnfStoreKey]; + if (!that.uuidData.vFModuleStoreKey) { + this._store.dispatch(createVFModuleInstance(formValues, that.uuidData.modelName, that.uuidData.serviceId, 0, that.uuidData.vnfStoreKey)); + } else { + this._store.dispatch(updateVFModuleInstance(formValues, that.uuidData.modelName, that.uuidData.serviceId, that.uuidData.vFModuleStoreKey, that.uuidData.vnfStoreKey)); + } + }; - constructor(private _basicControlGenerator: BasicControlGenerator, - private _vfModuleControlGenerator: VfModuleControlGenerator, - private _iframeService: IframeService, - private _defaultDataGeneratorService: DefaultDataGeneratorService, - private _aaiService: AaiService, - private _basicPopupService : BasicPopupService, - private _store: NgRedux<AppState>) { - + protected constructor( + protected _basicControlGenerator: BasicControlGenerator, + protected _vfModuleControlGenerator: VfModuleControlGenerator, + protected _iframeService: IframeService, + protected _defaultDataGeneratorService: DefaultDataGeneratorService, + protected _aaiService: AaiService, + protected _basicPopupService: BasicPopupService, + protected _store: NgRedux<AppState>) { } getInstance(serviceId: string, vnfStoreKey: string, vfModuleStoreKey: string): any { @@ -51,45 +61,6 @@ export class VfModulePopuopService implements GenericPopupInterface { return vfModules[this.uuidData['modelName']][vfModuleStoreKey]; } - getDynamicInputs(UUIDData : Object) : FormControlModel[]{ - let dynamic = this._defaultDataGeneratorService.getArbitraryInputs(this._store.getState().service.serviceHierarchy[UUIDData['serviceId']].vfModules[UUIDData['modelName']].inputs); - return this.getVFModuleDynamicInputs(dynamic, UUIDData); - } - - getVFModuleDynamicInputs(dynamicInputs : any, UUIDData : Object) : FormControlModel[] { - let result : FormControlModel[] = []; - if(dynamicInputs) { - let vfModuleInstance = null; - if (_.has(this._store.getState().service.serviceInstance[UUIDData['serviceId']].vnfs, UUIDData['vnfStoreKey']) && - _.has(this._store.getState().service.serviceInstance[UUIDData['serviceId']].vnfs[UUIDData['vnfStoreKey']].vfModules, UUIDData['modelName'])) { - vfModuleInstance = Object.assign({},this._store.getState().service.serviceInstance[UUIDData['serviceId']].vnfs[UUIDData['vnfStoreKey']].vfModules[UUIDData['modelName']][UUIDData['vfModuleStoreKey']]); - } - result = this._basicControlGenerator.getDynamicInputs(dynamicInputs, vfModuleInstance); - } - return result; - } - - - getGenericFormPopupDetails(serviceId: string, vnfStoreKey: string, vfModuleStoreKey: string, node: ITreeNode, uuidData: Object, isUpdateMode: boolean): FormPopupDetails { - - this.uuidData = uuidData; - this.instance = this.getInstance(serviceId, vnfStoreKey, vfModuleStoreKey); - this.getModelInformation(serviceId, uuidData['modelName']); - - return new FormPopupDetails(this, - PopupType.VFMODULE, - uuidData, - this.getTitle(isUpdateMode), - this.getSubLeftTitle(), - this.getSubRightTitle(), - this.getControls(serviceId, vnfStoreKey, vfModuleStoreKey, isUpdateMode), - this.getDynamicInputs(uuidData), - this.modelInformations, - (that, form: FormGroup) => {that.onSubmit(that, form);}, - (that: any, form: FormGroup) => {that.onCancel(that, form); } - ); - } - getModelInformation(serviceId: string, modelName: string) { this._aaiService.getServiceModelById(serviceId).subscribe((result: any) => { this.serviceModel = new ServiceModel(result); @@ -116,69 +87,125 @@ export class VfModulePopuopService implements GenericPopupInterface { }); } - getControls(serviceId: string, vnfStoreKey: string, vfModuleStoreKey: string, isUpdateMode: boolean) { - if (this._store.getState().service.serviceHierarchy[serviceId].service.vidNotions.instantiationType === 'Macro') { - return this._vfModuleControlGenerator.getMacroFormControls(serviceId, vnfStoreKey, vfModuleStoreKey, this.uuidData, isUpdateMode); - } else { - return this._vfModuleControlGenerator.getAlaCarteFormControls(serviceId, vnfStoreKey, vfModuleStoreKey, this.uuidData, isUpdateMode); - } + protected postSubmitIframeMessage(that) { + window.parent.postMessage({ + eventId: 'submitIframe', + data: { + serviceModelId: that.serviceModel.uuid + } + }, "*"); } + onCancel(that, form) { + form.reset(); + that._iframeService.removeClassCloseModal('content'); + this.closeDialogEvent.next(that); + } - onSubmit(that, form: FormGroup) { - form.value['instanceParams'] = form.value['instanceParams'] && [form.value['instanceParams']]; + getSubLeftTitle(): string { + return this.model.name; + } + + getSubRightTitle(): string { + return "Module (Heat stack) Instance Details"; + } + + abstract getTitle(isUpdateMode : boolean) : string; + abstract getControls(serviceId: string, vnfStoreKey: string, vfModuleStoreKey: string, isUpdateMode: boolean); + abstract getDynamicInputs(UUIDData : Object) : FormControlModel[]; + + getGenericFormPopupDetails(serviceId: string, vnfStoreKey: string, vfModuleStoreKey: string, node: ITreeNode, uuidData: Object, isUpdateMode: boolean): FormPopupDetails { + + this.uuidData = uuidData; + this.instance = this.getInstance(serviceId, vnfStoreKey, vfModuleStoreKey); + this.getModelInformation(serviceId, uuidData['modelName']); + + return new FormPopupDetails(this, + PopupType.VFMODULE, + uuidData, + this.getTitle(isUpdateMode), + this.getSubLeftTitle(), + this.getSubRightTitle(), + this.getControls(serviceId, vnfStoreKey, vfModuleStoreKey, isUpdateMode), + this.getDynamicInputs(uuidData), + this.modelInformations, + (that, form: FormGroup) => { + that.onSubmit(that, form); + }, + (that: any, form: FormGroup) => { + that.onCancel(that, form); + } + ); + } + + updateFormValueWithSupplementaryFile(form: FormGroup, that) { if (!_.isNil(form.controls['supplementaryFile_hidden_content']) && form.controls['supplementaryFile_hidden_content'].value) { form.value['supplementaryFileContent'] = JSON.parse(form.controls['supplementaryFile_hidden_content'].value); if (!_.isNil(form.controls['supplementaryFile_hidden'].value)) { form.value['supplementaryFileName'] = form.controls['supplementaryFile_hidden'].value.name; - } - else { + } else { form.value['supplementaryFileName'] = that.instance.supplementaryFileName; } - } - else { + } else { delete form.value['supplementaryFileContent']; delete form.value['supplementaryFileName']; } - that.storeVFModule(that, form.value); - window.parent.postMessage({ - eventId: 'submitIframe', - data: { - serviceModelId: that.serviceModel.uuid - } - }, "*"); - this.onCancel(that, form); } +} +@Injectable() +export class VfModulePopuopService extends VfModulePopupServiceBase implements GenericPopupInterface { + + + constructor(_basicControlGenerator: BasicControlGenerator, + _vfModuleControlGenerator: VfModuleControlGenerator, + _iframeService: IframeService, + _defaultDataGeneratorService: DefaultDataGeneratorService, + _aaiService: AaiService, + _basicPopupService : BasicPopupService, + _store: NgRedux<AppState>) { + super(_basicControlGenerator, _vfModuleControlGenerator, _iframeService, _defaultDataGeneratorService, _aaiService, _basicPopupService, _store); - onCancel(that, form) { - form.reset(); - that._iframeService.removeClassCloseModal('content'); - this.closeDialogEvent.next(that); } - storeVFModule = (that, formValues: any): void => { - formValues.modelInfo = new ModelInfo(that.model); - formValues.uuid = formValues.modelInfo.uuid; - formValues.isMissingData = false; - const vnf = that._store.getState().service.serviceInstance[that.uuidData.serviceId].vnfs[that.uuidData.vnfStoreKey]; + getDynamicInputs(UUIDData : Object) : FormControlModel[]{ + let dynamic = this._defaultDataGeneratorService.getArbitraryInputs(this._store.getState().service.serviceHierarchy[UUIDData['serviceId']].vfModules[UUIDData['modelName']].inputs); + return this.getVFModuleDynamicInputs(dynamic, UUIDData); + } - if (!that.uuidData.vFModuleStoreKey) { - this._store.dispatch(createVFModuleInstance(formValues, that.uuidData.modelName, that.uuidData.serviceId, 0, that.uuidData.vnfStoreKey)); - } else { - this._store.dispatch(updateVFModuleInstance(formValues, that.uuidData.modelName, that.uuidData.serviceId, that.uuidData.vFModuleStoreKey, that.uuidData.vnfStoreKey)); + getVFModuleDynamicInputs(dynamicInputs : any, UUIDData : Object) : FormControlModel[] { + let result : FormControlModel[] = []; + if(dynamicInputs) { + let vfModuleInstance = null; + if (_.has(this._store.getState().service.serviceInstance[UUIDData['serviceId']].vnfs, UUIDData['vnfStoreKey']) && + _.has(this._store.getState().service.serviceInstance[UUIDData['serviceId']].vnfs[UUIDData['vnfStoreKey']].vfModules, UUIDData['modelName'])) { + vfModuleInstance = Object.assign({},this._store.getState().service.serviceInstance[UUIDData['serviceId']].vnfs[UUIDData['vnfStoreKey']].vfModules[UUIDData['modelName']][UUIDData['vfModuleStoreKey']]); + } + result = this._basicControlGenerator.getDynamicInputs(dynamicInputs, vfModuleInstance); } - }; + return result; + } - getTitle(isUpdateMode : boolean) : string { - return isUpdateMode ? 'Edit Module (Heat stack)' : 'Set new Module (Heat stack)'; + + getControls(serviceId: string, vnfStoreKey: string, vfModuleStoreKey: string, isUpdateMode: boolean) { + if (this._store.getState().service.serviceHierarchy[serviceId].service.vidNotions.instantiationType === 'Macro') { + return this._vfModuleControlGenerator.getMacroFormControls(serviceId, vnfStoreKey, vfModuleStoreKey, this.uuidData, isUpdateMode); + } else { + return this._vfModuleControlGenerator.getAlaCarteFormControls(serviceId, vnfStoreKey, vfModuleStoreKey, this.uuidData, isUpdateMode); + } } - getSubLeftTitle(): string { - return this.model.name; + + onSubmit(that, form: FormGroup) { + form.value['instanceParams'] = form.value['instanceParams'] && [form.value['instanceParams']]; + this.updateFormValueWithSupplementaryFile(form, that); + that.storeVFModule(that, form.value); + this.postSubmitIframeMessage(that); + this.onCancel(that, form); } - getSubRightTitle(): string { - return "Module (Heat stack) Instance Details"; + getTitle(isUpdateMode : boolean) : string { + return isUpdateMode ? 'Edit Module (Heat stack)' : 'Set new Module (Heat stack)'; } + } diff --git a/vid-webpack-master/src/app/shared/components/genericFormPopup/genericFormServices/vfModuleUpgrade/vfModule.upgrade.popuop.service.ts b/vid-webpack-master/src/app/shared/components/genericFormPopup/genericFormServices/vfModuleUpgrade/vfModule.upgrade.popuop.service.ts new file mode 100644 index 000000000..bbfe8705e --- /dev/null +++ b/vid-webpack-master/src/app/shared/components/genericFormPopup/genericFormServices/vfModuleUpgrade/vfModule.upgrade.popuop.service.ts @@ -0,0 +1,95 @@ +import {Injectable} from "@angular/core"; +import {ITreeNode} from "angular-tree-component/dist/defs/api"; +import {FormGroup} from "@angular/forms"; +import {VfModulePopupServiceBase} from "../vfModule/vfModule.popuop.service"; +import {upgradeVFModule} from "../../../../storeUtil/utils/vfModule/vfModule.actions"; +import {SharedTreeService} from "../../../../../drawingBoard/service-planning/objectsToTree/shared.tree.service"; +import {NgRedux} from "@angular-redux/store"; +import {AppState} from "../../../../store/reducers"; +import {BasicControlGenerator} from "../../../genericForm/formControlsServices/basic.control.generator"; +import {VfModuleControlGenerator} from "../../../genericForm/formControlsServices/vfModuleGenerator/vfModule.control.generator"; +import {IframeService} from "../../../../utils/iframe.service"; +import {DefaultDataGeneratorService} from "../../../../services/defaultDataServiceGenerator/default.data.generator.service"; +import {AaiService} from "../../../../services/aaiService/aai.service"; +import {BasicPopupService} from "../basic.popup.service"; +import {FormControlModel} from "../../../../models/formControlModels/formControl.model"; +import {CheckboxFormControl} from "../../../../models/formControlModels/checkboxFormControl.model"; +import {FormControlType} from "../../../../models/formControlModels/formControlTypes.enum"; +import {mergeObjectByPathAction} from "../../../../storeUtil/utils/general/general.actions"; + +export enum UpgradeFormControlNames { + RETAIN_VOLUME_GROUPS = 'retainVolumeGroups', + RETAIN_ASSIGNMENTS = 'retainAssignments', + SDN_C_PRE_LOAD = 'sdncPreLoad', +} + +@Injectable() +export class VfModuleUpgradePopupService extends VfModulePopupServiceBase { + constructor(protected _basicControlGenerator: BasicControlGenerator, + protected _vfModuleControlGenerator: VfModuleControlGenerator, + protected _iframeService: IframeService, + protected _defaultDataGeneratorService: DefaultDataGeneratorService, + protected _aaiService: AaiService, + protected _basicPopupService: BasicPopupService, + protected _store: NgRedux<AppState>, + private _sharedTreeService: SharedTreeService) { + super(_basicControlGenerator, _vfModuleControlGenerator, _iframeService, _defaultDataGeneratorService, _aaiService, _basicPopupService, _store); + } + + node: ITreeNode; + + getDynamicInputs = () => null; + + getControls(serviceId: string, vnfStoreKey: string, vfModuleStoreKey: string, isUpdateMode: boolean): FormControlModel[] { + let result: FormControlModel[] =[ + this.getRetainAssignmentsControl(), + this.getRetainVolumeGroupsControl(), + this._basicControlGenerator.getSDNCControl(null) + ]; + + const vfModuleInstance = this._vfModuleControlGenerator.getVfModuleInstance(serviceId, vnfStoreKey, this.uuidData, isUpdateMode); + result = this._basicControlGenerator.concatSupplementaryFile(result, vfModuleInstance); + return result; + }; + + getTitle = (): string => 'Upgrade Module'; + + onSubmit(that, form: FormGroup) { + const node = that.uuidData.vfModule; + const serviceInstanceId: string = that.uuidData.serviceId; + const vnfStoreKey = node.parent.data.vnfStoreKey; + const modelName = node.data.modelName; + const dynamicModelName = node.data.dynamicModelName; + + this.updateFormValueWithSupplementaryFile(form, that); + + this._store.dispatch(upgradeVFModule(modelName, vnfStoreKey, serviceInstanceId, dynamicModelName)); + this._store.dispatch(mergeObjectByPathAction(['serviceInstance', serviceInstanceId, 'vnfs', vnfStoreKey, 'vfModules', modelName, dynamicModelName], form.value)); + this._sharedTreeService.upgradeBottomUp(node, serviceInstanceId); + + this.postSubmitIframeMessage(that); + this.onCancel(that, form); + } + + getRetainVolumeGroupsControl = (): CheckboxFormControl => { + return new CheckboxFormControl({ + type: FormControlType.CHECKBOX, + controlName: UpgradeFormControlNames.RETAIN_VOLUME_GROUPS, + displayName: 'Retain Volume Groups', + dataTestId: UpgradeFormControlNames.RETAIN_VOLUME_GROUPS, + value: true, + validations: [] + }) + }; + + getRetainAssignmentsControl = (): CheckboxFormControl => { + return new CheckboxFormControl({ + type: FormControlType.CHECKBOX, + controlName: UpgradeFormControlNames.RETAIN_ASSIGNMENTS, + displayName: 'Retain Assignments', + dataTestId: UpgradeFormControlNames.RETAIN_ASSIGNMENTS, + value: true, + validations: [] + }) + }; +} diff --git a/vid-webpack-master/src/app/shared/components/genericFormPopup/genericFormServices/vfModuleUpgrade/vfModule.upgrade.popup.service.spec.ts b/vid-webpack-master/src/app/shared/components/genericFormPopup/genericFormServices/vfModuleUpgrade/vfModule.upgrade.popup.service.spec.ts new file mode 100644 index 000000000..7c1cc084a --- /dev/null +++ b/vid-webpack-master/src/app/shared/components/genericFormPopup/genericFormServices/vfModuleUpgrade/vfModule.upgrade.popup.service.spec.ts @@ -0,0 +1,201 @@ +import {LogService} from "../../../../utils/log/log.service"; +import {NgRedux} from "@angular-redux/store"; +import {BasicControlGenerator, SDN_C_PRE_LOAD, SUPPLEMENTARY_FILE} from "../../../genericForm/formControlsServices/basic.control.generator"; +import {AaiService} from "../../../../services/aaiService/aai.service"; +import {HttpClient} from "@angular/common/http"; +import {GenericFormService} from "../../../genericForm/generic-form.service"; +import {FormBuilder, FormGroup} from "@angular/forms"; +import {IframeService} from "../../../../utils/iframe.service"; +import {DefaultDataGeneratorService} from "../../../../services/defaultDataServiceGenerator/default.data.generator.service"; +import {BasicPopupService} from "../basic.popup.service"; +import {VfModuleControlGenerator} from "../../../genericForm/formControlsServices/vfModuleGenerator/vfModule.control.generator"; +import {SdcUiServices} from "onap-ui-angular"; +import {FeatureFlagsService} from "../../../../services/featureFlag/feature-flags.service"; +import {getTestBed, TestBed} from "@angular/core/testing"; +import {UpgradeFormControlNames, VfModuleUpgradePopupService} from "./vfModule.upgrade.popuop.service"; +import {SharedTreeService} from "../../../../../drawingBoard/service-planning/objectsToTree/shared.tree.service"; +import {AppState} from "../../../../store/reducers"; +import {instance, mock} from "ts-mockito"; +import {GeneralActions} from "../../../../storeUtil/utils/general/general.actions"; +import {VfModuleActions} from "../../../../storeUtil/utils/vfModule/vfModule.actions"; +import {ServiceActions} from "../../../../storeUtil/utils/service/service.actions"; +import {FormControlModel} from "../../../../models/formControlModels/formControl.model"; + +class MockModalService<T> {} + +class MockAppStore<T> {} + +class MockReduxStore<T> { + getState() { + return { + service: { + serviceInstance : { + serviceId : { + vnfs : { + vnfStoreKey : { + vfModules: { + vfModuleName: { + vfModuleId : { + supplementaryFileName: "myFileName" + }}}}}}}} + }; + } + + dispatch() {} +} + +class MockFeatureFlagsService {} + +describe('VFModule popup service', () => { + let injector; + let service: VfModuleUpgradePopupService; + let genericFormService: GenericFormService; + let defaultDataGeneratorService: DefaultDataGeneratorService; + let fb: FormBuilder; + let iframeService: IframeService; + let store : NgRedux<AppState>; + + beforeAll(done => (async () => { + TestBed.configureTestingModule({ + providers: [ + VfModuleUpgradePopupService, + BasicControlGenerator, + VfModuleControlGenerator, + DefaultDataGeneratorService, + GenericFormService, + FormBuilder, + IframeService, + AaiService, + LogService, + BasicPopupService, + SharedTreeService, + {provide: FeatureFlagsService, useClass: MockFeatureFlagsService}, + {provide: NgRedux, useClass: MockReduxStore}, + {provide: HttpClient, useClass: MockAppStore}, + {provide: SdcUiServices.ModalService, useClass: MockModalService} + ] + }); + await TestBed.compileComponents(); + + injector = getTestBed(); + service = injector.get(VfModuleUpgradePopupService); + genericFormService = injector.get(GenericFormService); + defaultDataGeneratorService = injector.get(DefaultDataGeneratorService); + fb = injector.get(FormBuilder); + iframeService = injector.get(IframeService); + store = injector.get(NgRedux); + service.uuidData = { + modelName: 'vfModuleName', + vFModuleStoreKey: 'vfModuleId' + }; + + })().then(done).catch(done.fail)); + + test('getTitle should return the correct title', () => { + expect(service.getTitle()).toBe("Upgrade Module") + }); + + function findControlByName(controls: FormControlModel[], controlName: string) : FormControlModel { + return controls.find((control) => { + return control.controlName === controlName; + }); + } + + function getControlByNameAndCheckValue(controlName: string, expectedValue: any) { + const controls = service.getControls('serviceId', 'vnfStoreKey', 'vfModuleId', true); + const control = findControlByName(controls, controlName); + expect(control).toBeDefined(); + expect(control.value).toEqual(expectedValue); + } + + test('get controls should return retainAssignments control with true value', ()=> { + getControlByNameAndCheckValue(UpgradeFormControlNames.RETAIN_ASSIGNMENTS, true); + }); + + test('get controls should return retainVolumeGroup control with true value', ()=> { + getControlByNameAndCheckValue(UpgradeFormControlNames.RETAIN_VOLUME_GROUPS, true); + }); + + test('get controls should contain SUPPLEMENTARY_FILE controller', ()=> { + + //when + const controls = service.getControls('serviceId', 'vnfStoreKey', 'vfModuleId', true); + + //then + const control = findControlByName(controls, SUPPLEMENTARY_FILE); + expect(control).toBeDefined(); + expect(control.selectedFile).toBe("myFileName"); + }); + + test('on submit should call merge action of form value to vfModule', () => { + + //given + + const serviceId = "serviceId5"; + const vnfStoreKey = 'vnfStoreKey3'; + const modelName = 'modelA'; + const dynamicModelName = 'dynamicModel'; + const that = { + uuidData : { + vfModule : { + data : { + modelName, + dynamicModelName + }, + parent : { + data: { + vnfStoreKey + }}}, + serviceId + }, + serviceModel: { + uuid : "someUuid" + }, + _iframeService: { + removeClassCloseModal : jest.fn() + } + }; + + let mockFrom: FormGroup = mock(FormGroup); + let form = instance(mockFrom); + form.value = { + a: "value", + b: "another" + }; + form.controls = { + supplementaryFile_hidden_content : { + value: '{"c": "c", "d": 1}' + }, + supplementaryFile_hidden : { + value: { + name: "name" + } + } + }; + + let expectedMergePayload = { + a: "value", + b: "another", + supplementaryFileContent: {c: "c", d: 1}, + supplementaryFileName: "name" + }; + + spyOn(store, 'dispatch'); + + //when + service.onSubmit(that, form); + + //then + expect(store.dispatch).toBeCalledWith( + {type: GeneralActions.MERGE_OBJECT_BY_PATH, path: ['serviceInstance', serviceId, 'vnfs', vnfStoreKey, 'vfModules',modelName, dynamicModelName], payload:expectedMergePayload}); + expect(store.dispatch).toBeCalledWith( + {type: VfModuleActions.UPGRADE_VFMODULE, dynamicModelName: "dynamicModel", modelName: "modelA", serviceId: "serviceId5", vnfStoreKey: "vnfStoreKey3"}); + expect(store.dispatch).toBeCalledWith({type: ServiceActions.UPGRADE_SERVICE_ACTION, serviceUuid: "serviceId5"}); + + }); + + + test( 'get controls should return usePreload with false value', () => { + getControlByNameAndCheckValue(SDN_C_PRE_LOAD, false); + }); +}); diff --git a/vid-webpack-master/src/app/shared/components/genericFormPopup/genericFormServices/vnf/vnf.popup.service.spec.ts b/vid-webpack-master/src/app/shared/components/genericFormPopup/genericFormServices/vnf/vnf.popup.service.spec.ts index 6458e4f97..c4317f241 100644 --- a/vid-webpack-master/src/app/shared/components/genericFormPopup/genericFormServices/vnf/vnf.popup.service.spec.ts +++ b/vid-webpack-master/src/app/shared/components/genericFormPopup/genericFormServices/vnf/vnf.popup.service.spec.ts @@ -14,6 +14,7 @@ import {VnfControlGenerator} from "../../../genericForm/formControlsServices/vnf 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"; class MockAppStore<T> {} @@ -29,10 +30,7 @@ class MockReduxStore<T> { "FLAG_SHOW_VERIFY_SERVICE": false, "FLAG_SERVICE_MODEL_CACHE": true, "FLAG_ADVANCED_PORTS_FILTER": true, - "CREATE_INSTANCE_TEST": false, - "FLAG_SETTING_DEFAULTS_IN_DRAWING_BOARD": false, "FLAG_REGION_ID_FROM_REMOTE": true, - "EMPTY_DRAWING_BOARD_TEST": false, "FLAG_ADD_MSO_TESTAPI_FIELD": true }, "type": "[FLAGS] Update" @@ -2261,6 +2259,7 @@ describe('vnf new popup service', () => { LogService, BasicPopupService, VfModulePopuopService, + VfModuleUpgradePopupService, BasicControlGenerator, VnfControlGenerator, {provide: NgRedux, useClass: MockReduxStore}, @@ -2335,6 +2334,10 @@ describe('vnf new popup service', () => { expect(service.modelInformations[13].values).toEqual(['5']); }); + test('when there is no max instances in model , shell return maximum item with Unlimited text', () =>{ + expect(service.createMaximumToInstantiateModelInformationItem({}).values[0]).toEqual('Unlimited (default)'); + }); + test('getSubLeftTitle new vnf popup should return service model name', () => { service.uuidData = { serviceId: '6e59c5de-f052-46fa-aa7e-2fca9d674c44', diff --git a/vid-webpack-master/src/app/shared/components/genericFormPopup/genericFormServices/vnf/vnf.popup.service.ts b/vid-webpack-master/src/app/shared/components/genericFormPopup/genericFormServices/vnf/vnf.popup.service.ts index b23f74530..283603275 100644 --- a/vid-webpack-master/src/app/shared/components/genericFormPopup/genericFormServices/vnf/vnf.popup.service.ts +++ b/vid-webpack-master/src/app/shared/components/genericFormPopup/genericFormServices/vnf/vnf.popup.service.ts @@ -97,11 +97,21 @@ export class VnfPopupService implements GenericPopupInterface{ new ModelInformationItem("Service type", "serviceType", [this.serviceModel.serviceType]), new ModelInformationItem("Service role", "serviceRole", [this.serviceModel.serviceRole]), new ModelInformationItem("Minimum to instantiate", "vnf-min", [!_.isNil(this.model.min) ? this.model.min.toString() : '0'], "", false), - new ModelInformationItem("Maximum to instantiate", "vnf-max", [!_.isNil(this.model.max) ? this.model.max.toString() : '1'], "", false) + (this.createMaximumToInstantiateModelInformationItem(this.model)) ]; }) } + createMaximumToInstantiateModelInformationItem(model) { + return new ModelInformationItem( + "Maximum to instantiate", + "vnf-max", + [!_.isNil(model.max) ? model.max.toString() : Constants.ModelInfo.UNLIMITED_DEFAULT], + "", + false + ); + } + getSubLeftTitle(): string { return "VNF MODEL: " + this._store.getState().service.serviceHierarchy[this.uuidData['serviceId']].vnfs[this.uuidData['modelName']].name; } diff --git a/vid-webpack-master/src/app/shared/components/genericFormPopup/genericFormServices/vnfGroup/vnfGroup.popup.service.spec.ts b/vid-webpack-master/src/app/shared/components/genericFormPopup/genericFormServices/vnfGroup/vnfGroup.popup.service.spec.ts index 5397a7233..3a81e5b2b 100644 --- a/vid-webpack-master/src/app/shared/components/genericFormPopup/genericFormServices/vnfGroup/vnfGroup.popup.service.spec.ts +++ b/vid-webpack-master/src/app/shared/components/genericFormPopup/genericFormServices/vnfGroup/vnfGroup.popup.service.spec.ts @@ -14,6 +14,7 @@ import {FeatureFlagsService} from "../../../../services/featureFlag/feature-flag import {VnfGroupPopupService} from "./vnfGroup.popup.service"; import {VnfGroupControlGenerator} from "../../../genericForm/formControlsServices/vnfGroupGenerator/vnfGroup.control.generator"; import {getTestBed, TestBed} from "@angular/core/testing"; +import {VfModuleUpgradePopupService} from "../vfModuleUpgrade/vfModule.upgrade.popuop.service"; class MockAppStore<T> {} @@ -29,10 +30,7 @@ class MockReduxStore<T> { "FLAG_SHOW_VERIFY_SERVICE": false, "FLAG_SERVICE_MODEL_CACHE": true, "FLAG_ADVANCED_PORTS_FILTER": true, - "CREATE_INSTANCE_TEST": false, - "FLAG_SETTING_DEFAULTS_IN_DRAWING_BOARD": false, "FLAG_REGION_ID_FROM_REMOTE": true, - "EMPTY_DRAWING_BOARD_TEST": false, "FLAG_ADD_MSO_TESTAPI_FIELD": true }, "type": "[FLAGS] Update" @@ -2258,6 +2256,7 @@ describe('vnf group new popup service', () => { LogService, BasicPopupService, VfModulePopuopService, + VfModuleUpgradePopupService, BasicControlGenerator, VnfGroupControlGenerator, {provide: NgRedux, useClass: MockReduxStore}, diff --git a/vid-webpack-master/src/app/shared/components/genericFormPopup/instantiationTemplatesModal/instantiation.templates.modal.component.html b/vid-webpack-master/src/app/shared/components/genericFormPopup/instantiationTemplatesModal/instantiation.templates.modal.component.html new file mode 100644 index 000000000..19f641a56 --- /dev/null +++ b/vid-webpack-master/src/app/shared/components/genericFormPopup/instantiationTemplatesModal/instantiation.templates.modal.component.html @@ -0,0 +1,164 @@ +<div id="template-popup" class="modal-dialog"> + <div class="modal-content"> + <div class="modal-header"> + <button type="button" + class="close" + (click)="closeModal()">× + </button> + <span [attr.data-tests-id]="'template-modal-title'" + class="modal-title">Templates + </span> + </div> + <div class="modal-body templateModalBody"> + <div class="row description-section"> + <div class="col-md-6"> + <div [attr.data-tests-id]="'description-part-1'">The following list presents previous instantiations done for + this model in this version. + </div> + <div [attr.data-tests-id]="'description-part-2'">You may use one of them as a baseline for your instantiation + or start from scratch. + </div> + <div [attr.data-tests-id]="'description-part-3'">Once you selecting one allows you to change the data before + start instantiating. + </div> + </div> + <div class="col-md-6"> + <div class="col-md-6"> + </div> + <div class="col-md-6"> + <input + class="filter-input form-control input-text" + placeholder="Filter..." + [(ngModel)]="filterText"> + </div> + </div> + </div> + <div class="row" style="margin-left: 0;margin-right: 0;padding: 20px;"> + <table id="member-table" class="table table-bordered" style="table-layout: fixed"> + <thead class="thead-dark"> + <tr> + <th class="header-title" id="header-userId">User ID</th> + <th class="header-title" id="header-createDate" style="width: 21ch;">Date</th> + <th class="header-title" id="header-instanceName" style="max-width: 50ch;">Instance Name</th> + <th class="header-title" id="header-instantiationStatus" style="width: 30ch;">Instantiation Status</th> + <th class="header-title" id="header-summary">Summary</th> + <th class="header-title" id="header-region">Region</th> + <th class="header-title" id="header-tenant">Tenant</th> + <th class="header-title" id="header-aicZone">AIC Zone</th> + </tr> + </thead> + <tbody> + <tr class="member-table-row" + *ngFor="let item of filterTableData | searchFilter: filterText ;" + (click)="selectedJobId = item.jobId" + [ngClass]="{'selected' : selectedJobId === item.jobId}" + [attr.data-tests-id]="'row-' + item.jobId"> + <td> + <div> + <custom-ellipsis + [dataTestId]="'userId-' + item.jobId" + [id]="item.userId" + [value]="item.userId" + [breakWord]="true"> + </custom-ellipsis> + </div> + </td> + <td style="width: 21ch;"> + <div> + <custom-ellipsis + [dataTestId]="'createDate-' + item.jobId" + [id]="item.createDate" + [value]="item.createDate" + [breakWord]="true"> + </custom-ellipsis> + </div> + </td> + <td style="max-width: 50ch;"> + <div> + <custom-ellipsis + [showDots]="true" + [dataTestId]="'instanceName-' + item.jobId" + [id]="item.instanceName" + [value]="item.instanceName" + [breakWord]="true"> + </custom-ellipsis> + </div> + </td> + <td> + <div> + <custom-ellipsis + [showDots]="true" + [dataTestId]="'instantiationStatus-' + item.jobId" + [id]="item.instantiationStatus" + [value]="item.instantiationStatus" + [breakWord]="true"> + </custom-ellipsis> + </div> + </td> + <td> + <div> + <custom-ellipsis + [dataTestId]="'summary-' + item.jobId" + [id]="item.summary" + [value]="item.summary" + [breakWord]="true"> + </custom-ellipsis> + </div> + </td> + <td> + <div> + <custom-ellipsis + [showDots]="true" + [dataTestId]="'region-' + item.jobId" + [id]="item.region" + [value]="item.region" + [breakWord]="true"> + </custom-ellipsis> + </div> + </td> + <td> + <div> + <custom-ellipsis + [showDots]="true" + [dataTestId]="'tenant-' + item.jobId" + [id]="item.tenant" + [value]="item.tenant" + [breakWord]="true"> + </custom-ellipsis> + </div> + </td> + <td> + <div> + <custom-ellipsis + [dataTestId]="'aicZone-' + item.jobId" + [id]="item.aicZone" + [value]="item.aicZone" + [breakWord]="true"> + </custom-ellipsis> + </div> + </td> + </tr> + </tbody> + </table> + </div> + + </div> + <div class="modal-footer row" style="padding: 0"> + <div class="col-md-6"> + </div> + <div class="col-md-6" style="padding: 15px;padding-right: 35px;"> + <button + [disabled]="selectedJobId === null" + [attr.data-tests-id]="'LoadTemplateButton'" + type="button" class="btn btn-primary submit" + (click)="loadTemplate()"><span>Load Template</span> + </button> + <button + [attr.data-tests-id]="'startFromScratchButton'" + type="button" class="btn btn-success submit startFromScratchButton" + (click)="closeModal()"><span>Start from Scratch</span> + </button> + </div> + </div> + </div> +</div> diff --git a/vid-webpack-master/src/app/shared/components/genericFormPopup/instantiationTemplatesModal/instantiation.templates.modal.component.scss b/vid-webpack-master/src/app/shared/components/genericFormPopup/instantiationTemplatesModal/instantiation.templates.modal.component.scss new file mode 100644 index 000000000..267d2cee0 --- /dev/null +++ b/vid-webpack-master/src/app/shared/components/genericFormPopup/instantiationTemplatesModal/instantiation.templates.modal.component.scss @@ -0,0 +1,196 @@ +$grid-border: 1px #d2d2d2 solid; + +#template-popup { + color: #191919; + + thead { + background: #F8F8F8; + } + .left-panel { + background: #f2f2f2; + border-right: $grid-border; + } + + .header-common { + height: 100%; + align-items: center; + display: flex; + font-family: OpenSans-Semibold; + font-size: 12px; + } + + .header-text { + padding-left: 30px; + @extend .header-common; + } + + .header-left { + grid-area: header-left; + @extend .header-text; + @extend .left-panel; + border-bottom: $grid-border; + + span { + font-family: OpenSans-Regular; + font-size: 14px; + } + ; + } + + .header-right { + grid-area: header-right; + + @extend .header-text; + } + + .quantity-label { + grid-area: quantity-label; + @extend .header-common; + height: 100%; + font-family: OpenSans-Regular; + } + + input[type="number"]:hover::-webkit-inner-spin-button { + height: 20px; + } + + .popup-content { + display: grid; + grid-template-columns: 400px auto 30px 93px; + grid-template-rows: 50px calc(100vh - 180px); + grid-template-areas: "header-left header-right quantity-label quantity" "model-information instance-form instance-form instance-form"; + padding: 0; + } +} + +.modal { + background-color: #191919; + opacity: 0.8; +} + +.modal-dialog { + position: relative; + width: auto; + margin: 0; +} + +@media (min-width: 1150px) { + .popup-content { + grid-template-rows: 30px 680px; + } +} + +.modal-content { + border-radius: 0; + box-shadow: none; + border: none; + min-height: calc(100vh); + +} + +.modal-footer { + padding: 0; + position: absolute; + bottom: 0; + width: 100%; + + .cancel { + width: 120px; + height: 36px; + background: #ffffff; + border: 1px solid #009fdb; + border-radius: 2px; + + span { + font-family: OpenSans-Regular; + font-size: 14px; + color: #009fdb; + line-height: 16px; + } + } + + .startFromScratchButton { + width: 150px !important; + } + + .submit { + min-width: 120px; + height: 36px; + background: #009fdb; + border-radius: 2px; + border-color: #009fdb; + + + span { + font-family: OpenSans-Regular; + font-size: 14px; + color: #FFFFFF; + line-height: 16px; + } + } +} + +.modal-header { + background-color: #009fdb; + + padding-bottom: 13px; + padding-top: 13px; + padding-left: 29px; + padding-right: 21px; + + .close { + font-size: 32px; + font-weight: 200; + color: #d8d8d8; + text-shadow: none; + filter: none; + opacity: 1; + } + + .modal-title { + font-family: OpenSans-Regular; + font-size: 24px; + color: #fff; + line-height: 34px; + } +} + +.modal-body { + padding: 0; + height: calc(85vh); + + .description-section { + padding: 20px; + font-size: 20px; + } + + .filter-input { + float: right; + width: 50%; + } + + .details-item { + text-align: right; + } + + + td.loadTemplateButton { + text-align: center; + vertical-align: middle; + } + + td { + text-align: center; + vertical-align: middle; + padding-left: 5px; + padding-right: 5px; + } + + .member-table-row:hover { + background: #80808033 !important; + } + + .member-table-row.selected { + background: #8080808f !important; + } +} diff --git a/vid-webpack-master/src/app/shared/components/genericFormPopup/instantiationTemplatesModal/instantiation.templates.modal.component.ts b/vid-webpack-master/src/app/shared/components/genericFormPopup/instantiationTemplatesModal/instantiation.templates.modal.component.ts new file mode 100644 index 000000000..4d89750f9 --- /dev/null +++ b/vid-webpack-master/src/app/shared/components/genericFormPopup/instantiationTemplatesModal/instantiation.templates.modal.component.ts @@ -0,0 +1,57 @@ +import {Component, OnDestroy, OnInit} from "@angular/core"; +import {DialogComponent, DialogService} from "ng2-bootstrap-modal"; +import {IframeService} from "../../../utils/iframe.service"; +import {ActivatedRoute} from "@angular/router"; +import {ServiceInfoService} from "../../../server/serviceInfo/serviceInfo.service"; +import {InstantiationTemplatesModalService} from "./instantiation.templates.modal.service"; +import {InstantiationTemplatesRowModel} from "./instantiation.templates.row.model"; + +@Component({ + selector: 'template-modal', + templateUrl: 'instantiation.templates.modal.component.html', + styleUrls: ['instantiation.templates.modal.component.scss'] +}) + +export class InstantiationTemplatesModalComponent extends DialogComponent<string, boolean> implements OnInit, OnDestroy { + + selectedJobId : string = null; + templateModalComponentService: InstantiationTemplatesModalService; + originalTableData: InstantiationTemplatesRowModel[] = []; + filterTableData : InstantiationTemplatesRowModel[] = []; + filterText: string; + + constructor(dialogService: DialogService, + private _iframeService: IframeService, + private _serviceInfoService: ServiceInfoService, + private _templateModalComponentService: InstantiationTemplatesModalService, + private _route: ActivatedRoute) { + super(dialogService); + this.templateModalComponentService = _templateModalComponentService; + } + + ngOnInit(): void { + this.filterText = ''; + this._route + .queryParams + .subscribe(params => { + this._serviceInfoService.getServicesJobInfo(true, params['serviceModelId']).subscribe((jobs) => { + this.originalTableData = this._templateModalComponentService.convertResponseToUI(jobs); + this.filterTableData = this.originalTableData; + }); + }); + } + + loadTemplate = () => { + + }; + + + closeModal(): void { + this._iframeService.removeClassCloseModal('content'); + this.dialogService.removeDialog(this); + setTimeout(() => { + window.parent.postMessage("closeIframe", "*"); + }, 15); + + } +} diff --git a/vid-webpack-master/src/app/shared/components/genericFormPopup/instantiationTemplatesModal/instantiation.templates.modal.service.spec.ts b/vid-webpack-master/src/app/shared/components/genericFormPopup/instantiationTemplatesModal/instantiation.templates.modal.service.spec.ts new file mode 100644 index 000000000..1ff0f61e2 --- /dev/null +++ b/vid-webpack-master/src/app/shared/components/genericFormPopup/instantiationTemplatesModal/instantiation.templates.modal.service.spec.ts @@ -0,0 +1,133 @@ +import {getTestBed, TestBed} from '@angular/core/testing'; +import {HttpClientTestingModule, HttpTestingController} from '@angular/common/http/testing'; +import {InstantiationTemplatesModalService} from "./instantiation.templates.modal.service"; +import {AaiService} from "../../../services/aaiService/aai.service"; +import {ActivatedRoute} from "@angular/router"; +import {IframeService} from "../../../utils/iframe.service"; +import {NgRedux} from "@angular-redux/store"; +import {FeatureFlagsService} from "../../../services/featureFlag/feature-flags.service"; +import {InstantiationTemplatesRowModel} from "./instantiation.templates.row.model"; + + +class ActivatedRouteMock<T> { + queryParams() { + return { + serviceModelId: '6e59c5de-f052-46fa-aa7e-2fca9d674c44' + } + } +} + +class MockAppStore { + +} + +describe('instantiation templates modal service', () => { + let injector; + let service: InstantiationTemplatesModalService; + let httpMock: HttpTestingController; + let _aaiService: AaiService; + let _activatedRoute: ActivatedRoute; + + beforeAll(done => (async () => { + TestBed.configureTestingModule({ + imports: [HttpClientTestingModule], + providers: [InstantiationTemplatesModalService, + IframeService, + AaiService, + FeatureFlagsService, + {provide: ActivatedRoute, useClass: ActivatedRouteMock}, + {provide: NgRedux, useClass: MockAppStore} + ] + }); + await TestBed.compileComponents(); + + injector = getTestBed(); + service = injector.get(InstantiationTemplatesModalService); + httpMock = injector.get(HttpTestingController); + _aaiService = injector.get(AaiService); + _activatedRoute = injector.get(ActivatedRoute); + + })().then(done).catch(done.fail)); + + + test('service should be defined', () => { + expect(service).toBeDefined(); + }); + + + test('convertResponseToUI - should return table data', () => { + const jobs = [{ + "id": 5, + "created": 1524995555000, + "modified": 1524995556000, + "action": "INSTANTIATE", + "createdId": null, + "modifiedId": null, + "rowNum": null, + "auditUserId": null, + "auditTrail": null, + "jobId": "9f88fdb5-bb47-4bf3-8c5f-98f1ad0ec87c", + "templateId": "ce4ec177-cfc8-483e-8a2c-b7aea53fd740", + "userId": "16807000", + "msoRequestId": "c0011670-0e1a-4b74-945d-8bf5aede1d91", + "requestId": null, + "jobStatus": "FAILED", + "statusModifiedDate": 1524995555000, + "hidden": false, + "pause": false, + "owningEntityId": "aaa1", + "owningEntityName": "aaa1", + "project": "WATKINS", + "aicZoneId": "BAN1", + "aicZoneName": "VSDKYUTP-BAN1", + "tenantId": "1178612d2b394be4834ad77f567c0af2", + "tenantName": "AIN Web Tool-15-D-SSPtestcustome", + "regionId": "hvf6", + "regionName": null, + "serviceType": "TYLER SILVIA", + "subscriberName": "e433710f-9217-458d-a79d-1c7aff376d89", + "serviceInstanceId": null, + "serviceInstanceName": 'serviceInstanceName', + "serviceModelId": "e49fbd11-e60c-4a8e-b4bf-30fbe8f4fcc0", + "serviceModelName": "ComplexService", + "serviceModelVersion": "1.0", + "createdBulkDate": 1524995555000, + "isRetryEnabled": false + }]; + const tableRows: InstantiationTemplatesRowModel[] = service.convertResponseToUI(jobs); + expect(tableRows).toHaveLength(1); + expect(tableRows[0].userId).toEqual('16807000'); + expect((new Date(tableRows[0].createDate)).toISOString()).toEqual('2018-04-29T09:52:35.000Z'); + expect(tableRows[0].instanceName).toEqual('serviceInstanceName'); + expect(tableRows[0].instantiationStatus).toEqual('FAILED'); + expect(tableRows[0].region).toEqual('hvf6 (AAA1)'); + expect(tableRows[0].tenant).toEqual('AIN Web Tool-15-D-SSPtestcustome'); + expect(tableRows[0].aicZone).toEqual('VSDKYUTP-BAN1'); + expect(tableRows[0].jobId).toEqual('9f88fdb5-bb47-4bf3-8c5f-98f1ad0ec87c'); + }); + + + test('getCloudOwner should remove "-att" from owningEntityName : "att-owner', () => { + let result: InstantiationTemplatesRowModel = new InstantiationTemplatesRowModel({ + owningEntityName: 'att-owner', + regionId: 'regionId' + }); + expect(result.region).toEqual('regionId (OWNER)'); + }); + + test('getCloudOwner should not return owningEntityName if not exist', () => { + let result: InstantiationTemplatesRowModel = new InstantiationTemplatesRowModel({owningEntityName: null, regionId: 'regionId'}); + expect(result.region).toEqual('regionId'); + }); + + test('getInstanceName should return instance name id exist if not exist', () => { + let result: InstantiationTemplatesRowModel = new InstantiationTemplatesRowModel({serviceInstanceName: 'instanceName'}); + expect(result.instanceName).toEqual('instanceName'); + }); + + test('getInstanceName should return <Automatically generated> if instance name not exist', () => { + let result: InstantiationTemplatesRowModel = new InstantiationTemplatesRowModel({}); + expect(result.instanceName).toEqual('<Automatically generated>'); + }); + +}); diff --git a/vid-webpack-master/src/app/shared/components/genericFormPopup/instantiationTemplatesModal/instantiation.templates.modal.service.ts b/vid-webpack-master/src/app/shared/components/genericFormPopup/instantiationTemplatesModal/instantiation.templates.modal.service.ts new file mode 100644 index 000000000..7126da36a --- /dev/null +++ b/vid-webpack-master/src/app/shared/components/genericFormPopup/instantiationTemplatesModal/instantiation.templates.modal.service.ts @@ -0,0 +1,15 @@ +import {Injectable} from "@angular/core"; +import {InstantiationTemplatesRowModel} from "./instantiation.templates.row.model"; + +@Injectable() +export class InstantiationTemplatesModalService { + convertResponseToUI = (jobsResponse: any[]): InstantiationTemplatesRowModel[] => { + let tableRows: InstantiationTemplatesRowModel[] = []; + + jobsResponse.forEach((job) => { + tableRows.push(new InstantiationTemplatesRowModel(job)); + }); + + return tableRows; + }; +} diff --git a/vid-webpack-master/src/app/shared/components/genericFormPopup/instantiationTemplatesModal/instantiation.templates.row.model.ts b/vid-webpack-master/src/app/shared/components/genericFormPopup/instantiationTemplatesModal/instantiation.templates.row.model.ts new file mode 100644 index 000000000..08982cc67 --- /dev/null +++ b/vid-webpack-master/src/app/shared/components/genericFormPopup/instantiationTemplatesModal/instantiation.templates.row.model.ts @@ -0,0 +1,51 @@ +import * as moment from 'moment'; +import * as _ from 'lodash'; + +export class InstantiationTemplatesRowModel { + readonly jobId: string; + readonly userId ?: string; + readonly createDate ?: string; + readonly instanceName ?: string; + readonly instantiationStatus?: string; + readonly summary?: string; + readonly region?: string; + readonly tenant?: string; + readonly aicZone?: string; + + constructor(data) { + this.jobId = data.jobId; + this.userId = !_.isNil(data.created) ? data.userId : null; + this.createDate = !_.isNil(data.created) ? moment(data.created).format("YYYY-MM-DD HH:mm:ss") : null; + this.instanceName = this.getInstanceName(data.serviceInstanceName); + this.instantiationStatus = !_.isNil(data.jobStatus) ? data.jobStatus : null; + this.summary = null; + this.region = this.getRegion(data.regionId, data.owningEntityName); + this.tenant = !_.isNil(data.tenantName) ? data.tenantName : null; + this.aicZone = !_.isNil(data.aicZoneName) ? data.aicZoneName : null; + + } + + + /************************************************************************************************** + return the LCP region and in brackets the cloud owner removing the “att-“ with capital letters. + **************************************************************************************************/ + getCloudOwner = (owningEntityName: string): string => { + const splitByAtt: string[] = owningEntityName.split('att-'); + let owning: string = splitByAtt[splitByAtt.length - 1]; + return owning.toUpperCase(); + }; + + getRegion = (regionId: string, owningEntityName: string): string => { + const convertOwning = !_.isNil(owningEntityName) ? `(${this.getCloudOwner(owningEntityName)})` : ''; + return `${regionId} ${convertOwning}`.trim(); + }; + + + getInstanceName = (instanceName?: string): string => { + if (_.isNil(instanceName)) { + return '<Automatically generated>'; + } + return instanceName; + } +} + diff --git a/vid-webpack-master/src/app/shared/models/formControlModels/formPopupDetails.model.ts b/vid-webpack-master/src/app/shared/models/formControlModels/formPopupDetails.model.ts index 8ea3d2d0b..40d74d63f 100644 --- a/vid-webpack-master/src/app/shared/models/formControlModels/formPopupDetails.model.ts +++ b/vid-webpack-master/src/app/shared/models/formControlModels/formPopupDetails.model.ts @@ -14,6 +14,8 @@ export class FormPopupDetails { modelInformationItems: ModelInformationItem[]; onSubmit : (that : any, form: FormGroup , ...args) => void; onCancel : (that : any, form: FormGroup) => void; + onOtherAction: (that: any, form: FormGroup) => void; + constructor(that : any, popupTypeName : PopupType , @@ -25,7 +27,8 @@ export class FormPopupDetails { dynamicInputsControlList : FormControlModel[], modelInformationItems : ModelInformationItem[], onSubmit : (that : any, form : FormGroup, ...args) => void, - onCancel : (that : any, form : FormGroup) => void){ + onCancel: (that: any, form: FormGroup) => void, + onOtherAction?: (that: any, form: FormGroup) => void) { this.title = title; this.leftSubTitle = leftSubTitle; this.rightSubTitle = rightSubTitle; @@ -34,6 +37,7 @@ export class FormPopupDetails { this.modelInformationItems = modelInformationItems; this.onSubmit = onSubmit; this.onCancel = onCancel; + this.onOtherAction = onOtherAction; this.popupTypeName = popupTypeName; this.UUIDData = UUIDData; this.that = that; @@ -49,6 +53,7 @@ export enum PopupType { VNF_MACRO ='vnf macro', VNF_A_LA_CARTE = 'vnf a-la-carte', VFMODULE = 'vfModule', + VFMODULE_UPGRADE = 'vfModule_upgrade', NETWORK_MACRO = 'network_macro', VNF_GROUP = 'vnfGroup' } diff --git a/vid-webpack-master/src/app/shared/models/formControlModels/multiselectFormControl.model.ts b/vid-webpack-master/src/app/shared/models/formControlModels/multiselectFormControl.model.ts index b13745104..f5db715ba 100644 --- a/vid-webpack-master/src/app/shared/models/formControlModels/multiselectFormControl.model.ts +++ b/vid-webpack-master/src/app/shared/models/formControlModels/multiselectFormControl.model.ts @@ -7,10 +7,14 @@ export class MultiselectFormControl extends FormControlModel{ options$ : Observable<any[]>; args : string[]; onInit: (data : MultiselectFormControl, form: FormGroup) => Observable<any>; - selectedItems : string; + selectedItems : any[]; onInitSelectedItems : string[]; + selectedFieldName : string; ngValue : string; settings: {}; + onInitSelectedField?: string[]; + convertOriginalDataToArray? : (values)=> void; + limitSelection?: number; constructor(data) { @@ -18,10 +22,14 @@ export class MultiselectFormControl extends FormControlModel{ this.type = FormControlType.MULTI_SELECT; this.options$ = data.options; this.onInit = data.onInit; - this.selectedItems = data.selectedItems; + this.selectedItems = data.selectedItems || []; this.onInitSelectedItems = data.onInitSelectedItems ? data.onInitSelectedItems : null; this.ngValue = data.selectedField ? data.selectedField : 'id'; + this.selectedFieldName = data.selectedFieldName; this.settings = data.settings || {}; + this.onInitSelectedField = data.onInitSelectedField ? data.onInitSelectedField : null; + this.convertOriginalDataToArray = data.convertOriginalDataToArray ? data.convertOriginalDataToArray : null + this.limitSelection = data.limitSelection ? data.limitSelection : 1000; } } diff --git a/vid-webpack-master/src/app/shared/models/networkModel.ts b/vid-webpack-master/src/app/shared/models/networkModel.ts index 03f118eb7..b4879a59d 100644 --- a/vid-webpack-master/src/app/shared/models/networkModel.ts +++ b/vid-webpack-master/src/app/shared/models/networkModel.ts @@ -2,29 +2,30 @@ import { Level1Model, Level1ModelProperties, Level1ModelResponseInterface } from "./nodeModel"; -import {VfcInstanceGroupMap} from "./vfcInstanceGroupMap"; +import {Utils} from "../utils/utils"; -export interface NetworkProperties extends Level1ModelProperties{ +export interface NetworkProperties extends Level1ModelProperties { ecomp_generated_naming: string; network_role: string; } -export interface NetworkModelResponseInterface extends Level1ModelResponseInterface{ +export interface NetworkModelResponseInterface extends Level1ModelResponseInterface { properties: NetworkProperties; } -export class NetworkModel extends Level1Model{ +export class NetworkModel extends Level1Model { roles: string[] = []; properties: NetworkProperties; - constructor(networkJson?: NetworkModelResponseInterface){ + constructor(networkJson?: NetworkModelResponseInterface, flags?: { [key: string]: boolean }) { super(networkJson); - if(networkJson && networkJson.properties){ + if (networkJson && networkJson.properties) { this.properties = networkJson.properties; // expecting network_role to be a comma-saparated list this.roles = networkJson.properties.network_role ? networkJson.properties.network_role.split(',') : []; + this.max = Utils.getMaxFirstLevel(this.properties, flags); } } diff --git a/vid-webpack-master/src/app/shared/models/vfModule.ts b/vid-webpack-master/src/app/shared/models/vfModule.ts index c75202124..8f92ba428 100644 --- a/vid-webpack-master/src/app/shared/models/vfModule.ts +++ b/vid-webpack-master/src/app/shared/models/vfModule.ts @@ -1,4 +1,5 @@ import {NodeModel, NodeModelResponseInterface} from "./nodeModel"; +import {Utils} from "../utils/utils"; export interface Properties{ @@ -23,7 +24,7 @@ export class VfModule extends NodeModel { modelCustomizationName: string; volumeGroupAllowed : boolean; - constructor(vf?: VFModuleResponseInterface) { + constructor(vf?: VFModuleResponseInterface, flags?: { [key: string]: boolean }) { super(vf); if(vf){ this.customizationUuid = vf.customizationUuid; @@ -32,7 +33,7 @@ export class VfModule extends NodeModel { } if (vf && vf.properties) { this.min = vf.properties.minCountInstances; - this.max = vf.properties.maxCountInstances; + this.max = Utils.getMaxVfModule(vf.properties, flags); this.initial = vf.properties.initialCount; this.rollbackOnFailure = true } diff --git a/vid-webpack-master/src/app/shared/models/vnfModel.ts b/vid-webpack-master/src/app/shared/models/vnfModel.ts index 8389606b6..220dc0fc5 100644 --- a/vid-webpack-master/src/app/shared/models/vnfModel.ts +++ b/vid-webpack-master/src/app/shared/models/vnfModel.ts @@ -3,25 +3,24 @@ import { Level1ModelProperties, Level1ModelResponseInterface } from "./nodeModel"; +import {Utils} from "../utils/utils"; - - -export interface VnfProperties extends Level1ModelProperties{ +export interface VnfProperties extends Level1ModelProperties { ecomp_generated_naming: string; } -export interface VNFModelResponseInterface extends Level1ModelResponseInterface{ +export interface VNFModelResponseInterface extends Level1ModelResponseInterface { properties: VnfProperties; } -export class VNFModel extends Level1Model{ +export class VNFModel extends Level1Model { properties: VnfProperties; - constructor(vnfJson?: VNFModelResponseInterface) { + constructor(vnfJson?: VNFModelResponseInterface, flags?: { [key: string]: boolean }) { super(vnfJson); if (vnfJson) { this.properties = vnfJson.properties; + this.max = Utils.getMaxFirstLevel(this.properties, flags); } } - } diff --git a/vid-webpack-master/src/app/shared/pipes/searchFilter/search-filter.pipe.spec.ts b/vid-webpack-master/src/app/shared/pipes/searchFilter/search-filter.pipe.spec.ts new file mode 100644 index 000000000..2567cbf27 --- /dev/null +++ b/vid-webpack-master/src/app/shared/pipes/searchFilter/search-filter.pipe.spec.ts @@ -0,0 +1,17 @@ +import {SearchFilterPipe} from "./search-filter.pipe"; +import * as _ from 'lodash'; + +describe('Search filter pipe', () => { + + const items= [{'id':1, 'name': 'aaa'}, + {'id':12, 'name': 'bbb', 'children':{'first': 155, 'second': 2, 'third': 3}}, + {'id':3, 'name': 'ccc', 'children':{'first': 1, 'BbB': 'BbB', 'third': 3}}, + {'id':4, 'name': 'aad', 'children':{'first': 1, 'second': 2, 'third': 3}}]; + + test('should return items contains substring bb', () => { + let filter = new SearchFilterPipe(); + let res:any[] = filter.transform(items,'bb'); + expect(_.map(res, 'name' )).toEqual(['bbb','ccc']); + }); + +}); diff --git a/vid-webpack-master/src/app/shared/pipes/searchFilter/search-filter.pipe.ts b/vid-webpack-master/src/app/shared/pipes/searchFilter/search-filter.pipe.ts new file mode 100644 index 000000000..6e5cfc667 --- /dev/null +++ b/vid-webpack-master/src/app/shared/pipes/searchFilter/search-filter.pipe.ts @@ -0,0 +1,43 @@ +import {Pipe, PipeTransform} from '@angular/core'; +import * as _ from 'lodash'; + +@Pipe({ + name: 'searchFilter' +}) +export class SearchFilterPipe implements PipeTransform { + transform(items: Object[], searchText: string): any[] { + if (!items) return []; + if (!searchText) return items; + return items.filter((item: object) => { + + const deepFlatObject = this.flatten(item); + + const values = _.values(deepFlatObject).map((item: string) => { + return item.toString().toLowerCase() + }); + + return _.some(values, _.method('includes', searchText.toLowerCase())); + }); + } + + flatten = object => { + return Object.assign( + {}, + ...(function _flatten(objectBit, path = '') { + //spread the result into our return object + if(objectBit === null) return []; + return [].concat( + //concat everything into one level + + ...Object.keys(objectBit).map( + //iterate over object + key => + typeof objectBit[key] === 'object' //check if there is a nested object + ? _flatten(objectBit[key], `${path}/${key}`) //call itself if there is + : { [`${path}/${key}`]: objectBit[key] } //append object with it’s path as key + ) + ); + })(object) + ); + }; +} diff --git a/vid-webpack-master/src/app/shared/resolvers/recreate/recreate.resolver.spec.ts b/vid-webpack-master/src/app/shared/resolvers/recreate/recreate.resolver.spec.ts new file mode 100644 index 000000000..1a3a1ab3f --- /dev/null +++ b/vid-webpack-master/src/app/shared/resolvers/recreate/recreate.resolver.spec.ts @@ -0,0 +1,66 @@ +import {RecreateResolver} from "./recreate.resolver"; +import {getTestBed, TestBed} from '@angular/core/testing'; +import {NgRedux} from "@angular-redux/store"; +import {InstantiationTemplatesService} from "../../services/templateService/instantiationTemplates.service"; +import {AaiService} from "../../services/aaiService/aai.service"; +import {mock} from "ts-mockito"; +import {ServiceInstance} from "../../models/serviceInstance"; +import {HttpClientTestingModule} from "@angular/common/http/testing"; +import {FeatureFlagsService} from "../../services/featureFlag/feature-flags.service"; +import {convertToParamMap} from "@angular/router"; +import {of} from 'rxjs/observable/of' + +class MockAppStore<T> { + getState() {} +} + +describe('Recreate resolver', () => { + + let injector; + let recreateResolver: RecreateResolver; + let aaiService: AaiService; + let instantiationTemplatesService: InstantiationTemplatesService; + + beforeAll(done => (async () => { + TestBed.configureTestingModule({ + imports: [HttpClientTestingModule], + providers: [ + FeatureFlagsService, + InstantiationTemplatesService, + RecreateResolver, + AaiService, + {provide: NgRedux, useClass: MockAppStore}, + ] + }); + await TestBed.compileComponents(); + + injector = getTestBed(); + recreateResolver = injector.get(RecreateResolver); + aaiService = injector.get(AaiService); + instantiationTemplatesService = injector.get(InstantiationTemplatesService); + })().then(done).catch(done.fail)); + + test("when resolve() invoked -> then getServiceModelById and retrieveAndStoreInstantiationTemplateTopology are called", done => { + jest.spyOn(aaiService, 'getServiceModelById') + .mockReturnValue(of({})); + jest.spyOn(instantiationTemplatesService, 'retrieveAndStoreInstantiationTemplateTopology') + .mockReturnValue(of(mock(ServiceInstance))); + + recreateResolver.resolve(<any>{ + queryParamMap: + convertToParamMap({ + serviceModelId: "someServiceModelId", + jobId: "someJobId", + }) + }) + + .subscribe(() => { + expect(aaiService.getServiceModelById) + .toHaveBeenCalledWith("someServiceModelId"); + expect(instantiationTemplatesService.retrieveAndStoreInstantiationTemplateTopology) + .toHaveBeenCalledWith("someJobId", "someServiceModelId"); + done(); + }); + }) + +}); diff --git a/vid-webpack-master/src/app/shared/resolvers/recreate/recreate.resolver.ts b/vid-webpack-master/src/app/shared/resolvers/recreate/recreate.resolver.ts new file mode 100644 index 000000000..b7e952a64 --- /dev/null +++ b/vid-webpack-master/src/app/shared/resolvers/recreate/recreate.resolver.ts @@ -0,0 +1,29 @@ +import {Injectable} from "@angular/core"; +import {NgRedux} from "@angular-redux/store"; + +import {ActivatedRouteSnapshot, Resolve} from "@angular/router"; +import {Observable} from "rxjs"; +import {AppState} from "../../store/reducers"; +import {InstantiationTemplatesService} from "../../services/templateService/instantiationTemplates.service"; +import {forkJoin} from "rxjs/observable/forkJoin"; +import {AaiService} from "../../services/aaiService/aai.service"; +import {ServiceInstance} from "../../models/serviceInstance"; + +@Injectable() +export class RecreateResolver implements Resolve<Observable<[any, ServiceInstance]>> { + constructor(private _templateService: InstantiationTemplatesService, + private _aaiService: AaiService, + private _store: NgRedux<AppState>) { + } + + resolve(route: ActivatedRouteSnapshot): Observable<[any, ServiceInstance]> { + const serviceModelId: string = route.queryParamMap.get("serviceModelId"); + const jobId: string = route.queryParamMap.get("jobId"); + + let serviceModelApi = this._aaiService.getServiceModelById(serviceModelId); + let instantiationTemplateApi = this._templateService.retrieveAndStoreInstantiationTemplateTopology(jobId, serviceModelId); + + return forkJoin([serviceModelApi, instantiationTemplateApi]) + } + +} diff --git a/vid-webpack-master/src/app/shared/resolvers/viewEdit/viewEdit.resolver.ts b/vid-webpack-master/src/app/shared/resolvers/viewEdit/viewEdit.resolver.ts index 540e02fe6..075e18f25 100644 --- a/vid-webpack-master/src/app/shared/resolvers/viewEdit/viewEdit.resolver.ts +++ b/vid-webpack-master/src/app/shared/resolvers/viewEdit/viewEdit.resolver.ts @@ -10,6 +10,7 @@ import {ServiceInstance} from "../../models/serviceInstance"; import * as _ from "lodash"; import {ModelInfo} from "../../models/modelInfo"; import {FeatureFlagsService, Features} from "../../services/featureFlag/feature-flags.service"; +import {Utils} from "../../utils/utils"; @Injectable() export class ViewEditResolver implements Resolve<Observable<boolean>> { @@ -69,7 +70,7 @@ export class ViewEditResolver implements Resolve<Observable<boolean>> { }; setIsALaCarte(service: any, instantiationType) :void{ - service.isALaCarte = instantiationType === 'ALaCarte'; + service.isALaCarte = Utils.isALaCarte(instantiationType); }; } diff --git a/vid-webpack-master/src/app/shared/server/serviceInfo/serviceInfo.service.spec.ts b/vid-webpack-master/src/app/shared/server/serviceInfo/serviceInfo.service.spec.ts index 147434b1a..0234ea514 100644 --- a/vid-webpack-master/src/app/shared/server/serviceInfo/serviceInfo.service.spec.ts +++ b/vid-webpack-master/src/app/shared/server/serviceInfo/serviceInfo.service.spec.ts @@ -37,6 +37,25 @@ describe('Service Info Service', () => { }); }); + describe('#getServicesJobInfo', ()=> { + test('should call without serviceModelId', ()=>{ + let job: ServiceInfoModel = new ServiceInfoModel(); + + service.getServicesJobInfo().subscribe(); + const req = httpMock.expectOne(Constants.Path.SERVICES_JOB_INFO_PATH); + + expect(req.request.method).toBe('GET'); + }); + + test('should call with serviceModelId', ()=>{ + let job: ServiceInfoModel = new ServiceInfoModel(); + + service.getServicesJobInfo(true, "123").subscribe(); + const req = httpMock.expectOne(`${Constants.Path.SERVICES_JOB_INFO_PATH}?${Constants.Path.SERVICE_MODEL_ID}=123`); + expect(req.request.method).toBe('GET'); + }); + }); + describe('#getALaCarteJobAuditStatus Without params', ()=> { test('should return Observable<Object[]>', ()=>{ let job: ServiceInfoModel = new ServiceInfoModel(); diff --git a/vid-webpack-master/src/app/shared/server/serviceInfo/serviceInfo.service.ts b/vid-webpack-master/src/app/shared/server/serviceInfo/serviceInfo.service.ts index fe6ebc7ee..388afdba0 100644 --- a/vid-webpack-master/src/app/shared/server/serviceInfo/serviceInfo.service.ts +++ b/vid-webpack-master/src/app/shared/server/serviceInfo/serviceInfo.service.ts @@ -2,8 +2,6 @@ import {Injectable} from '@angular/core'; import {Observable} from 'rxjs'; import {ServiceInfoModel} from './serviceInfo.model'; import {HttpClient, HttpHeaders} from '@angular/common/http'; -import { of } from 'rxjs'; -import { map } from 'rxjs/operators'; import {Constants} from '../../utils/constants'; import {forkJoin} from "rxjs/observable/forkJoin"; import * as _ from 'lodash'; @@ -14,10 +12,11 @@ export class ServiceInfoService { constructor(private _http: HttpClient) { } - getServicesJobInfo(filterByUser : boolean, showSpinner: boolean = true): Observable<ServiceInfoModel[]> { + getServicesJobInfo(showSpinner: boolean = true, serviceModelId: string = null): Observable<ServiceInfoModel[]> { let pathQuery = Constants.Path.SERVICES_JOB_INFO_PATH; let headers = new HttpHeaders({'x-show-spinner': showSpinner.toString()}); - return this._http.get<ServiceInfoModel[]>(pathQuery, { headers: headers }).map(res => res ); + let params = serviceModelId ? {serviceModelId} : {}; + return this._http.get<ServiceInfoModel[]>(pathQuery, { headers: headers, params }); } deleteJob(jobId: string): Observable<any> { diff --git a/vid-webpack-master/src/app/shared/services/defaultDataServiceGenerator/default.data.generator.service.spec.ts b/vid-webpack-master/src/app/shared/services/defaultDataServiceGenerator/default.data.generator.service.spec.ts index a34f2fe20..03461eaa1 100644 --- a/vid-webpack-master/src/app/shared/services/defaultDataServiceGenerator/default.data.generator.service.spec.ts +++ b/vid-webpack-master/src/app/shared/services/defaultDataServiceGenerator/default.data.generator.service.spec.ts @@ -32,6 +32,7 @@ describe('Default Data Generator Service', () => { let result = service.generateVFModule(serviceHierarchy.vnfs[vnfUUID].vfModules[vnfModuleUUID], [], false, true); expect(result.isMissingData).toBeTruthy(); + expect(result.rollbackOnFailure).toBeTruthy(); }); test('generateVFModule should create vf module object', () => { @@ -51,6 +52,7 @@ describe('Default Data Generator Service', () => { expect(result.sdncPreReload).toBeNull(); expect(result.isMissingData).toBeTruthy(); expect(result.instanceParams).toEqual([{}]); + expect(result.rollbackOnFailure).toBeNull(); }); test('generateVNFData should create vnf object', () => { diff --git a/vid-webpack-master/src/app/shared/services/defaultDataServiceGenerator/default.data.generator.service.ts b/vid-webpack-master/src/app/shared/services/defaultDataServiceGenerator/default.data.generator.service.ts index b0baa82ec..a3cb475a0 100644 --- a/vid-webpack-master/src/app/shared/services/defaultDataServiceGenerator/default.data.generator.service.ts +++ b/vid-webpack-master/src/app/shared/services/defaultDataServiceGenerator/default.data.generator.service.ts @@ -186,7 +186,7 @@ export class DefaultDataGeneratorService { 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(this.store.getState().global.flags['FLAG_DEFAULT_VNF'] && min_vnf_instances_greater_than_0) + if(min_vnf_instances_greater_than_0) { this.createNetworkInstanceReduxIfNotExist( serviceId, @@ -203,7 +203,7 @@ export class DefaultDataGeneratorService { 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(this.store.getState().global.flags['FLAG_DEFAULT_VNF'] && min_vnf_group_instances_greater_than_0) + if(min_vnf_group_instances_greater_than_0) { this.createVnfGroupInstanceReduxIfNotExist( serviceId, @@ -244,7 +244,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(this.store.getState().global.flags['FLAG_DEFAULT_VNF'] && min_vnf_instances_greater_than_0) + if(min_vnf_instances_greater_than_0) { this.createVNFInstanceReduxIfNotExist( serviceId, @@ -328,6 +328,7 @@ export class DefaultDataGeneratorService { instanceParams ], 'trackById': DefaultDataGeneratorService.createRandomTrackById(), + 'rollbackOnFailure' : isALaCarte ? true : null, }; } diff --git a/vid-webpack-master/src/app/shared/services/featureFlag/feature-flags.service.ts b/vid-webpack-master/src/app/shared/services/featureFlag/feature-flags.service.ts index d6c37c936..74fcd8f87 100644 --- a/vid-webpack-master/src/app/shared/services/featureFlag/feature-flags.service.ts +++ b/vid-webpack-master/src/app/shared/services/featureFlag/feature-flags.service.ts @@ -8,11 +8,14 @@ export enum Features { FLAG_1902_VNF_GROUPING='FLAG_1902_VNF_GROUPING', FLAG_VF_MODULE_RESUME_STATUS_CREATE = 'FLAG_VF_MODULE_RESUME_STATUS_CREATE', FLAG_1911_INSTANTIATION_ORDER_IN_ASYNC_ALACARTE = 'FLAG_1911_INSTANTIATION_ORDER_IN_ASYNC_ALACARTE', - FLAG_1911_INSTANTIATION_ORDER_BUTTON_IN_ASYNC_ALACARTE = 'FLAG_1911_INSTANTIATION_ORDER_BUTTON_IN_ASYNC_ALACARTE', FLAG_1906_COMPONENT_INFO = 'FLAG_1906_COMPONENT_INFO', FLAG_1908_RESUME_MACRO_SERVICE = 'FLAG_1908_RESUME_MACRO_SERVICE', FLAG_FLASH_REPLACE_VF_MODULE ='FLAG_FLASH_REPLACE_VF_MODULE', - FLAG_FLASH_MORE_ACTIONS_BUTTON_IN_OLD_VIEW_EDIT ='FLAG_FLASH_MORE_ACTIONS_BUTTON_IN_OLD_VIEW_EDIT' + FLAG_FLASH_MORE_ACTIONS_BUTTON_IN_OLD_VIEW_EDIT ='FLAG_FLASH_MORE_ACTIONS_BUTTON_IN_OLD_VIEW_EDIT', + FLAG_2002_VFM_UPGRADE_ADDITIONAL_OPTIONS ='FLAG_2002_VFM_UPGRADE_ADDITIONAL_OPTIONS', + FLAG_2004_INSTANTIATION_STATUS_FILTER ='FLAG_2004_INSTANTIATION_STATUS_FILTER', + FLAG_2004_CREATE_ANOTHER_INSTANCE_FROM_TEMPLATE = 'FLAG_2004_CREATE_ANOTHER_INSTANCE_FROM_TEMPLATE', + FLAG_2004_TEMP_BUTTON_TO_INSTANTIATION_STATUS_FILTER ='FLAG_2004_TEMP_BUTTON_TO_INSTANTIATION_STATUS_FILTER', } @Injectable() @@ -24,6 +27,14 @@ export class FeatureFlagsService { return FeatureFlagsService.getFlagState(flag, this.store); } + public getAllFlags(): { [key: string]: boolean}{ + return this.store.getState().global.flags; + } + + public static getAllFlags(store: NgRedux<AppState>): { [key: string]: boolean}{ + return store.getState().global.flags; + } + /*static method for easy refactoring of code, so no injection of FeatureFlagsService is needed*/ public static getFlagState(flag: Features, store: NgRedux<AppState>):boolean { let storeStateGlobalFields = store.getState().global; diff --git a/vid-webpack-master/src/app/shared/services/templateService/instantiationTemplates.service.spec.ts b/vid-webpack-master/src/app/shared/services/templateService/instantiationTemplates.service.spec.ts new file mode 100644 index 000000000..80b113bde --- /dev/null +++ b/vid-webpack-master/src/app/shared/services/templateService/instantiationTemplates.service.spec.ts @@ -0,0 +1,51 @@ +import {HttpClientTestingModule, HttpTestingController} from "@angular/common/http/testing"; +import {InstantiationTemplatesService} from "./instantiationTemplates.service"; +import {mock} from "ts-mockito"; +import {NgRedux} from "@angular-redux/store"; +import {getTestBed, TestBed} from "@angular/core/testing"; +import {ServiceInstance} from "../../models/serviceInstance"; +import {Constants} from "../../utils/constants"; + +class MockAppStore<T> { + dispatch() {} + getState() {} +} + +describe("TemplateService", ()=>{ + let injector; + let httpMock: HttpTestingController; + let templateService: InstantiationTemplatesService; + + beforeAll( done => (async () => { + TestBed.configureTestingModule({ + imports: [HttpClientTestingModule], + providers: [ + InstantiationTemplatesService, + {provide: NgRedux, useClass: MockAppStore}, + ] + }); + await TestBed.compileComponents(); + + injector = getTestBed(); + httpMock = injector.get(HttpTestingController); + templateService = injector.get(InstantiationTemplatesService); + })().then(done).catch(done.fail)); + + describe ('#retrieveInstantiationTemplateTopology tests', () => { + test('when called -> retrieve template from backend', done => { + const mockedTemplate = mock(ServiceInstance); + const jobId: string = "some-random-job-id"; + + templateService.retrieveInstantiationTemplateTopology(jobId) + .subscribe((result: ServiceInstance) => { + expect(Object.is(result, mockedTemplate)).toBe(true); + done(); + }); + + httpMock + .expectOne(`${Constants.Path.INSTANTIATION_TEMPLATE_TOPOLOGY}/${jobId}`) + .flush(mockedTemplate); + }) + }) +}); + diff --git a/vid-webpack-master/src/app/shared/services/templateService/instantiationTemplates.service.ts b/vid-webpack-master/src/app/shared/services/templateService/instantiationTemplates.service.ts new file mode 100644 index 000000000..018e0d367 --- /dev/null +++ b/vid-webpack-master/src/app/shared/services/templateService/instantiationTemplates.service.ts @@ -0,0 +1,27 @@ +import {Injectable} from "@angular/core"; +import {HttpClient} from "@angular/common/http"; +import {NgRedux} from "@angular-redux/store"; +import {AppState} from "../../store/reducers"; +import {Observable} from "rxjs"; +import {ServiceInstance} from "../../models/serviceInstance"; +import {Constants} from "../../utils/constants"; +import {createServiceInstance} from "../../storeUtil/utils/service/service.actions"; +import {createServiceInstanceFromTemplate} from "../../storeUtil/utils/useTemplate/useTemplate.action"; + +@Injectable() +export class InstantiationTemplatesService { + constructor(private http: HttpClient, private store: NgRedux<AppState>) { + } + + retrieveInstantiationTemplateTopology(jobId: string): Observable<ServiceInstance> { + let pathQuery: string = `${Constants.Path.INSTANTIATION_TEMPLATE_TOPOLOGY}/${jobId}`; + return this.http.get<ServiceInstance>(pathQuery) + } + + public retrieveAndStoreInstantiationTemplateTopology(jobId: string, serviceModelId: string): Observable<ServiceInstance> { + return this.retrieveInstantiationTemplateTopology(jobId).do((instantiationTemplate: ServiceInstance) => { + this.store.dispatch(createServiceInstanceFromTemplate(instantiationTemplate, serviceModelId)); + }); + }; + +} diff --git a/vid-webpack-master/src/app/shared/shared.module.ts b/vid-webpack-master/src/app/shared/shared.module.ts index 93452256b..273dff472 100644 --- a/vid-webpack-master/src/app/shared/shared.module.ts +++ b/vid-webpack-master/src/app/shared/shared.module.ts @@ -42,6 +42,7 @@ import {NetworkPopupService} from "./components/genericFormPopup/genericFormServ import {NetworkControlGenerator} from "./components/genericForm/formControlsServices/networkGenerator/network.control.generator"; import {BasicPopupService} from "./components/genericFormPopup/genericFormServices/basic.popup.service"; import {VfModulePopuopService} from "./components/genericFormPopup/genericFormServices/vfModule/vfModule.popuop.service"; +import {VfModuleUpgradePopupService} from "./components/genericFormPopup/genericFormServices/vfModuleUpgrade/vfModule.upgrade.popuop.service"; import {VfModuleControlGenerator} from "./components/genericForm/formControlsServices/vfModuleGenerator/vfModule.control.generator"; import {OrderByPipe} from "./pipes/order/orderBy.pipe"; import {ServicePopupService} from "./components/genericFormPopup/genericFormServices/service/service.popup.service"; @@ -72,6 +73,12 @@ import {ClickOutsideDirective} from "./directives/clickOutside/clickOutside.dire import {DynamicInputsComponent} from "./components/dynamic-inputs/dynamic-inputs.component"; import {DynamicInputLabelPipe} from "./pipes/dynamicInputLabel/dynamic-input-label.pipe"; import {ModelInformationService} from "./components/model-information/model-information.service"; +import {MultiselectFormControlService} from "./components/formControls/component/multiselect/multiselect.formControl.service"; +import {InstantiationTemplatesModalComponent} from "./components/genericFormPopup/instantiationTemplatesModal/instantiation.templates.modal.component"; +import {InstantiationTemplatesModalService} from "./components/genericFormPopup/instantiationTemplatesModal/instantiation.templates.modal.service"; +import {SearchFilterPipe} from "./pipes/searchFilter/search-filter.pipe"; +import {RecreateResolver} from "./resolvers/recreate/recreate.resolver"; +import {InstantiationTemplatesService} from "./services/templateService/instantiationTemplates.service"; @NgModule({ @@ -112,6 +119,7 @@ import {ModelInformationService} from "./components/model-information/model-info SafePipe, ObjectToArrayPipe, DataFilterPipe, + SearchFilterPipe, InputFormControlComponent, FormControlMessageErrorComponent, GenericFormPopupComponent, @@ -123,7 +131,8 @@ import {ModelInformationService} from "./components/model-information/model-info SvgComponent, ErrorMsgComponent, DynamicInputsComponent, - DynamicInputLabelPipe + DynamicInputLabelPipe, + InstantiationTemplatesModalComponent ], exports: [ PopoverComponent, @@ -143,6 +152,7 @@ import {ModelInformationService} from "./components/model-information/model-info SafePipe, ObjectToArrayPipe, DataFilterPipe, + SearchFilterPipe, InputFormControlComponent, FormControlMessageErrorComponent, GenericFormPopupComponent, @@ -158,7 +168,8 @@ import {ModelInformationService} from "./components/model-information/model-info ], entryComponents : [ GenericFormPopupComponent, - SearchElementsModalComponent + SearchElementsModalComponent, + InstantiationTemplatesModalComponent ], providers: [ ServiceInfoService, @@ -173,6 +184,8 @@ import {ModelInformationService} from "./components/model-information/model-info FlagsResolve, ViewEditResolver, RetryResolver, + RecreateResolver, + InstantiationTemplatesService, ServiceControlGenerator, ServicePopupService, VnfControlGenerator, @@ -181,6 +194,7 @@ import {ModelInformationService} from "./components/model-information/model-info CustomValidators, NetworkPopupService, VfModulePopuopService, + VfModuleUpgradePopupService, NetworkControlGenerator, VnfGroupControlGenerator, VnfGroupPopupService, @@ -193,7 +207,10 @@ import {ModelInformationService} from "./components/model-information/model-info ElementsTableService, ErrorMsgService, DataFilterPipe, + SearchFilterPipe, ModelInformationService, + MultiselectFormControlService, + InstantiationTemplatesModalService ] }) export class SharedModule { diff --git a/vid-webpack-master/src/app/shared/storeUtil/utils/general/general.actions.ts b/vid-webpack-master/src/app/shared/storeUtil/utils/general/general.actions.ts index 7a10eba0a..79dd3c717 100644 --- a/vid-webpack-master/src/app/shared/storeUtil/utils/general/general.actions.ts +++ b/vid-webpack-master/src/app/shared/storeUtil/utils/general/general.actions.ts @@ -5,6 +5,7 @@ import {ServiceType} from "../../../models/serviceType"; import {ITreeNode} from "angular-tree-component/dist/defs/api"; export enum GeneralActions { + MERGE_OBJECT_BY_PATH = "MERGE_OBJECT_BY_PATH", UPDATE_LCP_REGIONS_AND_TENANTS = "UPDATE_LCP_REGIONS_AND_TENANTS", UPDATE_SUBSCRIBERS = "UPDATE_SUBSCRIBERS", UPDATE_PRODUCT_FAMILIES = "UPDATE_PRODUCT_FAMILIES", @@ -78,6 +79,11 @@ export interface UpdateServiceTypesAction extends Action { subscriberId: string; } +export interface MergeObjectByPathAction extends Action{ + path: String[]; + payload: object; +} + export const updateLcpRegionsAndTenants: ActionCreator<UpdateLcpRegionsAndTenantsAction> = lcpRegionsAndTenants => ({ type: GeneralActions.UPDATE_LCP_REGIONS_AND_TENANTS, lcpRegionsAndTenants: lcpRegionsAndTenants @@ -147,4 +153,10 @@ export const updateServiceTypes: ActionCreator<UpdateServiceTypesAction> = (serv subscriberId: subscriberId }); +export const mergeObjectByPathAction : ActionCreator<MergeObjectByPathAction> = (path, payload) => ({ + type: GeneralActions.MERGE_OBJECT_BY_PATH, + path, + payload +}); + diff --git a/vid-webpack-master/src/app/shared/storeUtil/utils/general/general.reducers.spec.ts b/vid-webpack-master/src/app/shared/storeUtil/utils/general/general.reducers.spec.ts index ed456314e..952a92842 100644 --- a/vid-webpack-master/src/app/shared/storeUtil/utils/general/general.reducers.spec.ts +++ b/vid-webpack-master/src/app/shared/storeUtil/utils/general/general.reducers.spec.ts @@ -1,22 +1,12 @@ import {LcpRegion} from "../../../models/lcpRegion"; import {Tenant} from "../../../models/tenant"; import {generalReducer} from "./general.reducers"; -import { - ChangeInstanceCounterAction, - RemoveInstanceAction, - DuplicateBulkInstancesAction, - GeneralActions, - UpdateAicZonesAction, - UpdateCategoryParametersAction, - UpdateProductFamiliesAction, - UpdateServiceTypesAction, - UpdateSubscribersAction, - UpdateUserIdAction, UpdateNetworkCollectionFunction, -} from "./general.actions"; +import {ChangeInstanceCounterAction, DuplicateBulkInstancesAction, GeneralActions, MergeObjectByPathAction, RemoveInstanceAction, UpdateAicZonesAction, UpdateCategoryParametersAction, UpdateNetworkCollectionFunction, UpdateProductFamiliesAction, UpdateServiceTypesAction, UpdateSubscribersAction, UpdateUserIdAction,} from "./general.actions"; import {SelectOption} from "../../../models/selectOption"; import {ServiceType} from "../../../models/serviceType"; import {ITreeNode} from "angular-tree-component/dist/defs/api"; import {VnfInstance} from "../../../models/vnfInstance"; +import each from "jest-each"; describe('generalReducer', () => { test('#UPDATE_LCP_REGIONS_AND_TENANTS : should update lcp region and tenants', () => { @@ -370,6 +360,45 @@ describe('generalReducer', () => { expect(state).toBeDefined(); expect(state['networkFunctions']).toBeDefined(); }); + + const originalMockObject = { + remain: 'forever', + obsolete: 'toBeChange' + }; + + each([ + [ + ['serviceInstance', 'serviceModelId', 'vnfs'], + { + remain: 'forever', + obsolete: 'newValue', + newField: 'newValue2' + } + ], + [ + ['serviceInstance', 'nowhere', 'somewhere'], + originalMockObject + ], + ]). + test('#MERGE_OBJECT_BY_PATH: should update some object by path %s', (path, expected) => { + let state = generalReducer(<any>{serviceInstance : { + 'serviceModelId' : { + vnfs : originalMockObject, + existingVNFCounterMap : {} + } + }}, + <MergeObjectByPathAction>{ + type: GeneralActions.MERGE_OBJECT_BY_PATH, + path, + payload: { + obsolete: 'newValue', + newField: 'newValue2' + } + }); + + expect(state).toBeDefined(); + expect(state.serviceInstance['serviceModelId'].vnfs).toEqual(expected); + }); }); diff --git a/vid-webpack-master/src/app/shared/storeUtil/utils/general/general.reducers.ts b/vid-webpack-master/src/app/shared/storeUtil/utils/general/general.reducers.ts index 5b265dbfd..f87a97397 100644 --- a/vid-webpack-master/src/app/shared/storeUtil/utils/general/general.reducers.ts +++ b/vid-webpack-master/src/app/shared/storeUtil/utils/general/general.reducers.ts @@ -1,12 +1,5 @@ import {Action} from "redux"; -import { - ChangeInstanceCounterAction, RemoveInstanceAction, DuplicateBulkInstancesAction, - GeneralActions, - UpdateAicZonesAction, UpdateCategoryParametersAction, - UpdateLcpRegionsAndTenantsAction, UpdateNetworkCollectionFunction, - UpdateProductFamiliesAction, UpdateServiceTypesAction, - UpdateSubscribersAction, UpdateUserIdAction -} from "./general.actions"; +import {ChangeInstanceCounterAction, DuplicateBulkInstancesAction, GeneralActions, MergeObjectByPathAction, RemoveInstanceAction, UpdateAicZonesAction, UpdateCategoryParametersAction, UpdateLcpRegionsAndTenantsAction, UpdateNetworkCollectionFunction, UpdateProductFamiliesAction, UpdateServiceTypesAction, UpdateSubscribersAction, UpdateUserIdAction} from "./general.actions"; import {TypeNodeInformation} from "../../../../drawingBoard/service-planning/typeNodeInformation.model"; import * as _ from "lodash"; import {ITreeNode} from "angular-tree-component/dist/defs/api"; @@ -92,6 +85,19 @@ export function generalReducer(state: ServiceState, action: Action) : ServiceSta return newState; } + case GeneralActions.MERGE_OBJECT_BY_PATH : { + const mergeObjectByPathAction = <MergeObjectByPathAction>action; + let newState = _.cloneDeep(state); + let targetObject = _.get(newState, <any>mergeObjectByPathAction.path); + if (targetObject) { + targetObject = _.merge(targetObject, mergeObjectByPathAction.payload); + } + else { + console.error(`Can't find object at ${mergeObjectByPathAction.path.join()}`) + } + return newState; + } + } } diff --git a/vid-webpack-master/src/app/shared/storeUtil/utils/main.reducer.ts b/vid-webpack-master/src/app/shared/storeUtil/utils/main.reducer.ts index c02049eb9..a135563eb 100644 --- a/vid-webpack-master/src/app/shared/storeUtil/utils/main.reducer.ts +++ b/vid-webpack-master/src/app/shared/storeUtil/utils/main.reducer.ts @@ -9,9 +9,11 @@ import {VNFActions} from "./vnf/vnf.actions"; import {vnfReducer} from "./vnf/vnf.reducers"; import {generalReducer} from "./general/general.reducers"; import {serviceReducer} from "./service/service.reducers"; +import {useTemplateReducer} from "./useTemplate/useTemplate.reducer"; import {networkReducer} from "./network/network.reducers"; import {vfModuleReducer} from "./vfModule/vfModule.reducers"; import {ServiceInstance} from "../../models/serviceInstance"; +import {UseTemplateActions} from "./useTemplate/useTemplate.action"; import {SelectOptionInterface} from "../../models/selectOption"; import {ServiceType} from "../../models/serviceType"; import {VnfGroupActions} from "./vnfGroup/vnfGroup.actions"; @@ -72,6 +74,8 @@ export const MainReducer = function (state: ServiceState = initialState, action: return crReducer(state, action); }else if(Object.values(NcfActions).includes(action.type)){ return ncfReducer(state, action); + } else if(Object.values(UseTemplateActions).includes(action.type)) { + return useTemplateReducer(state, action); } else { return Object.assign({}, state); } diff --git a/vid-webpack-master/src/app/shared/storeUtil/utils/useTemplate/useTemplate.action.ts b/vid-webpack-master/src/app/shared/storeUtil/utils/useTemplate/useTemplate.action.ts new file mode 100644 index 000000000..2cfd38482 --- /dev/null +++ b/vid-webpack-master/src/app/shared/storeUtil/utils/useTemplate/useTemplate.action.ts @@ -0,0 +1,17 @@ +import {Action, ActionCreator} from "redux"; +import {ServiceInstance} from "../../../models/serviceInstance"; + +export enum UseTemplateActions { + CREATE_SERVICE_INSTANCE_FROM_TEMPLATE = 'CREATE_SERVICE_INSTANCE_FROM_TEMPLATE', +} + +export interface CreateServiceInstanceFromTemplate extends Action { + serviceInstantiationTemplate?: ServiceInstance; + serviceModelId?: string; +} + +export const createServiceInstanceFromTemplate: ActionCreator<CreateServiceInstanceFromTemplate> = (serviceInstantiationTemplate, serviceModelId) => ({ + type: UseTemplateActions.CREATE_SERVICE_INSTANCE_FROM_TEMPLATE, + serviceInstantiationTemplate: serviceInstantiationTemplate, + serviceModelId: serviceModelId +}); diff --git a/vid-webpack-master/src/app/shared/storeUtil/utils/useTemplate/useTemplate.reducer.spec.ts b/vid-webpack-master/src/app/shared/storeUtil/utils/useTemplate/useTemplate.reducer.spec.ts new file mode 100644 index 000000000..e0f46e13a --- /dev/null +++ b/vid-webpack-master/src/app/shared/storeUtil/utils/useTemplate/useTemplate.reducer.spec.ts @@ -0,0 +1,18 @@ +import {ServiceInstance} from "../../../models/serviceInstance"; +import {useTemplateReducer} from "./useTemplate.reducer"; +import {CreateServiceInstanceFromTemplate, UseTemplateActions} from "./useTemplate.action"; + +test('#CREATE_SERVICE_INSTANCE_FROM_TEMPLATE should add new service instance from template to redux ', () => { + let serviceFromTemplateInstance: ServiceInstance = <any>{ + instanceName: 'templateInstanceName' + }; + let serviceState = useTemplateReducer(<any>{ + serviceInstance:{}}, + <CreateServiceInstanceFromTemplate> { + type: UseTemplateActions.CREATE_SERVICE_INSTANCE_FROM_TEMPLATE, + serviceModelId: 'serviceModelID', + serviceInstantiationTemplate: serviceFromTemplateInstance, + }) + expect (serviceState).toBeDefined(); + expect (serviceState.serviceInstance['serviceModelID'].instanceName).toEqual('templateInstanceName'); +}); diff --git a/vid-webpack-master/src/app/shared/storeUtil/utils/useTemplate/useTemplate.reducer.ts b/vid-webpack-master/src/app/shared/storeUtil/utils/useTemplate/useTemplate.reducer.ts new file mode 100644 index 000000000..5a06d6e0a --- /dev/null +++ b/vid-webpack-master/src/app/shared/storeUtil/utils/useTemplate/useTemplate.reducer.ts @@ -0,0 +1,20 @@ +import {ServiceState} from "../main.reducer"; +import {Action} from "redux"; +import { + createServiceInstanceFromTemplate, + CreateServiceInstanceFromTemplate, + UseTemplateActions +} from "./useTemplate.action"; +import * as _ from "lodash"; + +export function useTemplateReducer(state: ServiceState, action: Action) : ServiceState { + switch (action.type) { + case UseTemplateActions.CREATE_SERVICE_INSTANCE_FROM_TEMPLATE : { + const updateServiceInstanceFromTemplateAction = <CreateServiceInstanceFromTemplate>action; + const uuid = updateServiceInstanceFromTemplateAction.serviceModelId; + let newState = _.cloneDeep(state); + newState.serviceInstance[uuid] = updateServiceInstanceFromTemplateAction.serviceInstantiationTemplate; + return newState; + } + } +} diff --git a/vid-webpack-master/src/app/shared/storeUtil/utils/vfModule/vfModule.actions.ts b/vid-webpack-master/src/app/shared/storeUtil/utils/vfModule/vfModule.actions.ts index 59e5ee1fa..70c10c429 100644 --- a/vid-webpack-master/src/app/shared/storeUtil/utils/vfModule/vfModule.actions.ts +++ b/vid-webpack-master/src/app/shared/storeUtil/utils/vfModule/vfModule.actions.ts @@ -9,6 +9,8 @@ export enum VfModuleActions { UPDATE_VFMODULE_POSITION = "UPDATE_VFMODULE_POSITION", UPGRADE_VFMODULE = "UPGRADE_VFMODULE", UNDO_UPGRADE_VFMODULE_ACTION = "UNDO_UPGRADE_VFMODULE_ACTION", + UPDATE_VFMODULE_FEILD = "UPDATE_VFMODULE_FEILD", + DELETE_VFMODULE_FEILD = "DELETE_VFMODULE_FEILD", } @@ -62,6 +64,23 @@ export interface UndoUpgradeVfModuleInstanceAction extends Action { dynamicModelName: string; } +export interface UpdateVFModuleField extends Action { + modelName : string; + vnfStoreKey : string; + serviceId: string; + dynamicModelName: string; + fieldName: string; + fieldValue : any; +} + +export interface DeleteVFModuleField extends Action { + modelName : string; + vnfStoreKey : string; + serviceId: string; + dynamicModelName: string; + deleteFieldName: string; +} + export interface UndoDeleteActionVfModuleInstanceAction extends Action { dynamicModelName: string; vnfStoreKey : string; @@ -132,3 +151,22 @@ export const undoUgradeVFModule: ActionCreator<UndoUpgradeVfModuleInstanceAction vnfStoreKey, serviceId }); + +export const updateVFModuleField: ActionCreator<UpdateVFModuleField> = (modelName, vnfStoreKey, serviceId, dynamicModelName, fieldName, fieldValue) => ({ + type: VfModuleActions.UPDATE_VFMODULE_FEILD, + dynamicModelName, + modelName, + vnfStoreKey, + serviceId, + fieldName, + fieldValue +}); + +export const deleteVFModuleField: ActionCreator<DeleteVFModuleField> = (modelName, vnfStoreKey, serviceId, dynamicModelName, deleteFieldName) => ({ + type: VfModuleActions.DELETE_VFMODULE_FEILD, + dynamicModelName, + modelName, + vnfStoreKey, + serviceId, + deleteFieldName +}); diff --git a/vid-webpack-master/src/app/shared/storeUtil/utils/vfModule/vfModule.reducers.spec.ts b/vid-webpack-master/src/app/shared/storeUtil/utils/vfModule/vfModule.reducers.spec.ts index ee0edb0a7..7b890b715 100644 --- a/vid-webpack-master/src/app/shared/storeUtil/utils/vfModule/vfModule.reducers.spec.ts +++ b/vid-webpack-master/src/app/shared/storeUtil/utils/vfModule/vfModule.reducers.spec.ts @@ -1,9 +1,9 @@ import { CreateVFModuleInstanceAction, - DeleteActionVfModuleInstanceAction, + DeleteActionVfModuleInstanceAction, DeleteVFModuleField, DeleteVfModuleInstanceAction, UndoDeleteActionVfModuleInstanceAction, - UpdateVFModluePosition, + UpdateVFModluePosition, UpdateVFModuleField, UpgradeVfModuleInstanceAction, VfModuleActions } from "./vfModule.actions"; @@ -14,12 +14,13 @@ import {ServiceInstanceActions} from "../../../models/serviceInstanceActions"; describe('vfModuleReducer', () => { test('#REMOVE_VNF_MODULE_INSTANCE : should delete existing vnf module by dynamicModelName', () => { - let state = vfModuleReducer(<any>{serviceInstance : { - 'serviceModelId' : { - vnfs : { - 'vfName' : { - vfModules : { - 'modelName' : { + let state = vfModuleReducer(<any>{ + serviceInstance: { + 'serviceModelId': { + vnfs: { + 'vfName': { + vfModules: { + 'modelName': { 'dynamicModelName1': {}, 'dynamicModelName2': {}, } @@ -27,13 +28,14 @@ describe('vfModuleReducer', () => { } } } - }}, + } + }, <DeleteVfModuleInstanceAction>{ type: VfModuleActions.REMOVE_VNF_MODULE_INSTANCE, - modelName : 'modelName', - vfName : 'vfName', - vnfStoreKey : 'vfName', - serviceModelId : 'serviceModelId', + modelName: 'modelName', + vfName: 'vfName', + vnfStoreKey: 'vfName', + serviceModelId: 'serviceModelId', dynamicModelName: 'dynamicModelName1' }); @@ -43,26 +45,27 @@ describe('vfModuleReducer', () => { }); test('#DELETE_LAST_VNF_MODULE_INSTANCE : should delete existing vnf module', () => { - let state = vfModuleReducer(<any>{serviceInstance : { - 'serviceModelId' : { - vnfs : { - 'vfName' : { - vfModules : { - 'modelName' : { - 'dynamicModelName': { - } + let state = vfModuleReducer(<any>{ + serviceInstance: { + 'serviceModelId': { + vnfs: { + 'vfName': { + vfModules: { + 'modelName': { + 'dynamicModelName': {} } } } } } - }}, + } + }, <DeleteVfModuleInstanceAction>{ type: VfModuleActions.REMOVE_VNF_MODULE_INSTANCE, - modelName : 'modelName', - vfName : 'vfName', - vnfStoreKey : 'vfName', - serviceModelId : 'serviceModelId', + modelName: 'modelName', + vfName: 'vfName', + vnfStoreKey: 'vfName', + serviceModelId: 'serviceModelId', dynamicModelName: 'dynamicModelName' }); @@ -70,28 +73,29 @@ describe('vfModuleReducer', () => { expect(state.serviceInstance['serviceModelId'].vnfs['vfName'].vfModules['modelName']).not.toBeDefined(); }); - test('#CREATE_VF_MODULE: should create new vfModule to existing VNF', ()=>{ - let vfModuleInstance : VfModuleInstance = new VfModuleInstance(); + test('#CREATE_VF_MODULE: should create new vfModule to existing VNF', () => { + let vfModuleInstance: VfModuleInstance = new VfModuleInstance(); vfModuleInstance.instanceName = 'instanceName'; vfModuleInstance.isMissingData = false; vfModuleInstance.volumeGroupName = 'volumeGroupName'; - let vfModule = vfModuleReducer(<any>{serviceInstance : { - 'serviceUuid' : { - vnfs : { - 'vnfStoreKey' : { - 'vfModules' : { - } + let vfModule = vfModuleReducer(<any>{ + serviceInstance: { + 'serviceUuid': { + vnfs: { + 'vnfStoreKey': { + 'vfModules': {} } } } - }}, + } + }, <CreateVFModuleInstanceAction>{ type: VfModuleActions.CREATE_VF_MODULE, - vfId : 'vfId', - vfInstance : new VfModuleInstance(), - vnfStoreKey : 'vnfStoreKey', - serviceUuid : 'serviceUuid', - index : 1 + vfId: 'vfId', + vfInstance: new VfModuleInstance(), + vnfStoreKey: 'vnfStoreKey', + serviceUuid: 'serviceUuid', + index: 1 }).serviceInstance['serviceUuid'].vnfs['vnfStoreKey'].vfModules; let firstVfModuleName = Object.keys(vfModule)[0]; @@ -99,23 +103,64 @@ describe('vfModuleReducer', () => { expect(vfModule[firstVfModuleName].isMissingData).toBeFalsy(); }); - test('#UPDATE_VF_MODULE: should update existing VFModule', ()=>{ - let vfModuleInstance : VfModuleInstance = new VfModuleInstance(); - vfModuleInstance.instanceName = 'instanceName'; - vfModuleInstance.isMissingData = false; - vfModuleInstance.volumeGroupName = 'volumeGroupName'; - let vfModule = vfModuleReducer(<any>{ - serviceHierarchy : { - 'serviceModelId' : {} + test('#UPDATE_VFMODULE_FEILD: should update field with some value', () => { + const newFieldName = 'newFieldName'; + const newFieldValue = 'newFieldValue'; + let oldState = { + serviceHierarchy: { + 'serviceModelId': {} + }, + serviceInstance: { + 'serviceModelId': { + vnfs: { + 'vnfStoreKey': { + vfModules: { + 'modelName': { + 'dynamicModelName1': { + isMissingData: true, + action: 'None' + }, + 'dynamicModelName2': {}, + } + } + } + } + } + } + }; + + let newState = vfModuleReducer(<any>oldState, + <UpdateVFModuleField>{ + type: VfModuleActions.UPDATE_VFMODULE_FEILD, + dynamicModelName: 'dynamicModelName1', + vnfStoreKey: 'vnfStoreKey', + serviceId: 'serviceModelId', + modelName: 'modelName', + fieldName: newFieldName, + fieldValue: newFieldValue + }); + + let vfModule = newState.serviceInstance['serviceModelId'].vnfs['vnfStoreKey'].vfModules['modelName']['dynamicModelName1']; + + expect(vfModule[newFieldName]).toEqual(newFieldValue); + }); + + test('#DELETE_VFMODULE_FEILD: should update field with some value', () => { + const deleteFieldName = 'deleteFieldName'; + let oldState = { + serviceHierarchy: { + 'serviceModelId': {} }, - serviceInstance : { - 'serviceModelId' : { - vnfs : { - 'vfName' : { - vfModules : { - 'modelName' : { + serviceInstance: { + 'serviceModelId': { + vnfs: { + 'vnfStoreKey': { + vfModules: { + 'modelName': { 'dynamicModelName1': { - isMissingData : true + isMissingData: true, + [deleteFieldName]: true, + action: 'None' }, 'dynamicModelName2': {}, } @@ -123,192 +168,197 @@ describe('vfModuleReducer', () => { } } } - }}, - <CreateVFModuleInstanceAction>{ - type: VfModuleActions.UPDATE_VF_MODULE, - vfId : 'modelName', - vfInstance : new VfModuleInstance(), - vnfStoreKey : 'vfName', - dynamicModelName : 'dynamicModelName1', - serviceUuid : 'serviceModelId', - index : 1 - }).serviceInstance['serviceModelId'].vnfs['vfName'].vfModules; + } + }; + let newState = vfModuleReducer(<any>oldState, + <DeleteVFModuleField>{ + type: VfModuleActions.DELETE_VFMODULE_FEILD, + dynamicModelName: 'dynamicModelName1', + vnfStoreKey: 'vnfStoreKey', + serviceId: 'serviceModelId', + modelName: 'modelName', + deleteFieldName: deleteFieldName, + }); - let firstVfModuleName = Object.keys(vfModule)[0]; - expect(vfModule[firstVfModuleName]).toBeDefined(); - expect(vfModule[firstVfModuleName].isMissingData).toBeFalsy(); - }); + let vfModule = newState.serviceInstance['serviceModelId'].vnfs['vnfStoreKey'].vfModules['modelName']['dynamicModelName1']; + expect(vfModule[deleteFieldName]).toBeUndefined(); +}); - test('#UPDATE_VFMODULE_POSITION: should update position', ()=>{ - let vfModule = vfModuleReducer(<any>{ - serviceHierarchy : { - 'serviceModelId' : {} - }, - serviceInstance : { - 'serviceModelId' : { - vnfs : { - 'vfName' : { - vfModules : { - 'modelName' : { - 'dynamicModelName': { - isMissingData : true - } +test('#UPDATE_VFMODULE_POSITION: should update position', () => { + let vfModule = vfModuleReducer(<any>{ + serviceHierarchy: { + 'serviceModelId': {} + }, + serviceInstance: { + 'serviceModelId': { + vnfs: { + 'vfName': { + vfModules: { + 'modelName': { + 'dynamicModelName': { + isMissingData: true } } } } } - }}, - <UpdateVFModluePosition>{ - type: VfModuleActions.UPDATE_VFMODULE_POSITION, - node: { - position : 1, - dynamicModelName : "dynamicModelName", - modelName : "modelName" - }, - instanceId : "serviceModelId", - vnfStoreKey : "vfName" + } + } + }, + <UpdateVFModluePosition>{ + type: VfModuleActions.UPDATE_VFMODULE_POSITION, + node: { + position: 1, + dynamicModelName: "dynamicModelName", + modelName: "modelName" + }, + instanceId: "serviceModelId", + vnfStoreKey: "vfName" - }).serviceInstance['serviceModelId'].vnfs['vfName'].vfModules["modelName"]["dynamicModelName"]; - - expect(vfModule.position).toEqual(1); - }); + }).serviceInstance['serviceModelId'].vnfs['vfName'].vfModules["modelName"]["dynamicModelName"]; + expect(vfModule.position).toEqual(1); +}); - test('#DELETE_ACTION_VF_MODULE_INSTANCE', ()=>{ - let vfModule = vfModuleReducer(<any>{ - serviceHierarchy : { - 'serviceModelId' : {} - }, - serviceInstance : { - 'serviceModelId' : { - vnfs : { - 'vnfStoreKey' : { - vfModules : { - 'modelName' : { - 'dynamicModelName1': { - isMissingData : true, - action : 'None' - }, - 'dynamicModelName2': {}, - } +test('#DELETE_ACTION_VF_MODULE_INSTANCE', () => { + let vfModule = vfModuleReducer(<any>{ + serviceHierarchy: { + 'serviceModelId': {} + }, + serviceInstance: { + 'serviceModelId': { + vnfs: { + 'vnfStoreKey': { + vfModules: { + 'modelName': { + 'dynamicModelName1': { + isMissingData: true, + action: 'None' + }, + 'dynamicModelName2': {}, } } } } - }}, - <DeleteActionVfModuleInstanceAction>{ - type: VfModuleActions.DELETE_ACTION_VF_MODULE_INSTANCE, - dynamicModelName: 'dynamicModelName1', - vnfStoreKey : 'vnfStoreKey', - serviceId: 'serviceModelId' - }).serviceInstance['serviceModelId'].vnfs['vnfStoreKey'].vfModules['modelName']['dynamicModelName1']; + } + } + }, + <DeleteActionVfModuleInstanceAction>{ + type: VfModuleActions.DELETE_ACTION_VF_MODULE_INSTANCE, + dynamicModelName: 'dynamicModelName1', + vnfStoreKey: 'vnfStoreKey', + serviceId: 'serviceModelId' + }).serviceInstance['serviceModelId'].vnfs['vnfStoreKey'].vfModules['modelName']['dynamicModelName1']; - console.log(vfModule.action); - expect(vfModule).toBeDefined(); - expect(vfModule.isMissingData).toBeTruthy(); - expect(vfModule.action).toEqual(ServiceInstanceActions.None_Delete); - }); + console.log(vfModule.action); + expect(vfModule).toBeDefined(); + expect(vfModule.isMissingData).toBeTruthy(); + expect(vfModule.action).toEqual(ServiceInstanceActions.None_Delete); +}); - test('#UNDO_DELETE_ACTION_VF_MODULE_INSTANCE', ()=>{ - let vfModule = vfModuleReducer(<any>{ - serviceHierarchy : { - 'serviceModelId' : {} - }, - serviceInstance : { - 'serviceModelId' : { - vnfs : { - 'vnfStoreKey' : { - vfModules : { - 'modelName' : { - 'dynamicModelName1': { - isMissingData : true, - action : 'None_Delete' - }, - 'dynamicModelName2': {}, - } +test('#UNDO_DELETE_ACTION_VF_MODULE_INSTANCE', () => { + let vfModule = vfModuleReducer(<any>{ + serviceHierarchy: { + 'serviceModelId': {} + }, + serviceInstance: { + 'serviceModelId': { + vnfs: { + 'vnfStoreKey': { + vfModules: { + 'modelName': { + 'dynamicModelName1': { + isMissingData: true, + action: 'None_Delete' + }, + 'dynamicModelName2': {}, } } } } - }}, - <UndoDeleteActionVfModuleInstanceAction>{ - type: VfModuleActions.UNDO_DELETE_ACTION_VF_MODULE_INSTANCE, - dynamicModelName: 'dynamicModelName1', - vnfStoreKey : 'vnfStoreKey', - serviceId: 'serviceModelId' - }).serviceInstance['serviceModelId'].vnfs['vnfStoreKey'].vfModules['modelName']['dynamicModelName1']; + } + } + }, + <UndoDeleteActionVfModuleInstanceAction>{ + type: VfModuleActions.UNDO_DELETE_ACTION_VF_MODULE_INSTANCE, + dynamicModelName: 'dynamicModelName1', + vnfStoreKey: 'vnfStoreKey', + serviceId: 'serviceModelId' + }).serviceInstance['serviceModelId'].vnfs['vnfStoreKey'].vfModules['modelName']['dynamicModelName1']; - console.log(vfModule.action); - expect(vfModule).toBeDefined(); - expect(vfModule.action).toEqual(ServiceInstanceActions.None); - }); + console.log(vfModule.action); + expect(vfModule).toBeDefined(); + expect(vfModule.action).toEqual(ServiceInstanceActions.None); +}); - test('#UPGRADE_VFMODULE', ()=>{ - let vfModule = vfModuleReducer(<any>{ - serviceHierarchy : { - 'serviceModelId' : {} - }, - serviceInstance : { - 'serviceModelId' : { - vnfs : { - 'vnfStoreKey' : { - vfModules : { - 'modelName' : { - 'dynamicModelName1': { - isMissingData : true, - action : 'None' - }, - 'dynamicModelName2': {}, - } +test('#UPGRADE_VFMODULE', () => { + let vfModule = vfModuleReducer(<any>{ + serviceHierarchy: { + 'serviceModelId': {} + }, + serviceInstance: { + 'serviceModelId': { + vnfs: { + 'vnfStoreKey': { + vfModules: { + 'modelName': { + 'dynamicModelName1': { + isMissingData: true, + action: 'None' + }, + 'dynamicModelName2': {}, } } } } - }}, - <UpgradeVfModuleInstanceAction>{ - type: VfModuleActions.UPGRADE_VFMODULE, - dynamicModelName: 'dynamicModelName1', - vnfStoreKey : 'vnfStoreKey', - serviceId: 'serviceModelId', - modelName: 'modelName' - }).serviceInstance['serviceModelId'].vnfs['vnfStoreKey'].vfModules['modelName']['dynamicModelName1']; + } + } + }, + <UpgradeVfModuleInstanceAction>{ + type: VfModuleActions.UPGRADE_VFMODULE, + dynamicModelName: 'dynamicModelName1', + vnfStoreKey: 'vnfStoreKey', + serviceId: 'serviceModelId', + modelName: 'modelName' + }).serviceInstance['serviceModelId'].vnfs['vnfStoreKey'].vfModules['modelName']['dynamicModelName1']; - expect(vfModule.action).toEqual(ServiceInstanceActions.None_Upgrade); - }); + expect(vfModule.action).toEqual(ServiceInstanceActions.None_Upgrade); +}); - test('#UNDO_UPGRADE_VFMODULE', ()=>{ - let vfModule = vfModuleReducer(<any>{ - serviceHierarchy : { - 'serviceModelId' : {} - }, - serviceInstance : { - 'serviceModelId' : { - vnfs : { - 'vnfStoreKey' : { - vfModules : { - 'modelName' : { - 'dynamicModelName1': { - isMissingData : true, - action : 'None_Upgrade' - }, - 'dynamicModelName2': {}, - } +test('#UNDO_UPGRADE_VFMODULE', () => { + let vfModule = vfModuleReducer(<any>{ + serviceHierarchy: { + 'serviceModelId': {} + }, + serviceInstance: { + 'serviceModelId': { + vnfs: { + 'vnfStoreKey': { + vfModules: { + 'modelName': { + 'dynamicModelName1': { + isMissingData: true, + action: 'None_Upgrade' + }, + 'dynamicModelName2': {}, } } } } - }}, - <UpgradeVfModuleInstanceAction>{ - type: VfModuleActions.UNDO_UPGRADE_VFMODULE_ACTION, - dynamicModelName: 'dynamicModelName1', - vnfStoreKey : 'vnfStoreKey', - serviceId: 'serviceModelId', - modelName: 'modelName' - }).serviceInstance['serviceModelId'].vnfs['vnfStoreKey'].vfModules['modelName']['dynamicModelName1']; - - expect(vfModule.action).toEqual(ServiceInstanceActions.None); - }); + } + } + }, + <UpgradeVfModuleInstanceAction>{ + type: VfModuleActions.UNDO_UPGRADE_VFMODULE_ACTION, + dynamicModelName: 'dynamicModelName1', + vnfStoreKey: 'vnfStoreKey', + serviceId: 'serviceModelId', + modelName: 'modelName' + }).serviceInstance['serviceModelId'].vnfs['vnfStoreKey'].vfModules['modelName']['dynamicModelName1']; + expect(vfModule.action).toEqual(ServiceInstanceActions.None); }); + +}) +; diff --git a/vid-webpack-master/src/app/shared/storeUtil/utils/vfModule/vfModule.reducers.ts b/vid-webpack-master/src/app/shared/storeUtil/utils/vfModule/vfModule.reducers.ts index a7aadba41..1bb2b15fd 100644 --- a/vid-webpack-master/src/app/shared/storeUtil/utils/vfModule/vfModule.reducers.ts +++ b/vid-webpack-master/src/app/shared/storeUtil/utils/vfModule/vfModule.reducers.ts @@ -1,10 +1,10 @@ import {Action} from "redux"; import * as _ from "lodash"; import { - CreateVFModuleInstanceAction, DeleteActionVfModuleInstanceAction, - DeleteVfModuleInstanceAction, UndoDeleteActionVfModuleInstanceAction, UpdateVFModluePosition, + CreateVFModuleInstanceAction, DeleteActionVfModuleInstanceAction, DeleteVFModuleField, + DeleteVfModuleInstanceAction, UndoDeleteActionVfModuleInstanceAction, UpdateVFModluePosition, UpdateVFModuleField, UpdateVFModuleInstanceAction, UpgradeVfModuleInstanceAction, - VfModuleActions + VfModuleActions, } from "./vfModule.actions"; import {ServiceInstance} from "../../../models/serviceInstance"; import {VfModuleMap} from "../../../models/vfModulesMap"; @@ -145,6 +145,26 @@ export function vfModuleReducer(state: ServiceState , action: Action) : ServiceS } return clonedState; } + case VfModuleActions.UPDATE_VFMODULE_FEILD : { + let clonedState = _.cloneDeep(state); + let updateFieldAction = <UpdateVFModuleField> action; + + clonedState.serviceInstance[updateFieldAction.serviceId] + .vnfs[updateFieldAction.vnfStoreKey] + .vfModules[updateFieldAction.modelName][updateFieldAction.dynamicModelName][updateFieldAction.fieldName] = updateFieldAction.fieldValue; + + return clonedState; + } + case VfModuleActions.DELETE_VFMODULE_FEILD : { + let clonedState = _.cloneDeep(state); + let deleteAction = <DeleteVFModuleField> action; + + delete clonedState.serviceInstance[deleteAction.serviceId] + .vnfs[deleteAction.vnfStoreKey] + .vfModules[deleteAction.modelName][deleteAction.dynamicModelName][deleteAction.deleteFieldName]; + + return clonedState; + } } } diff --git a/vid-webpack-master/src/app/shared/utils/constants.ts b/vid-webpack-master/src/app/shared/utils/constants.ts index 400a4d8dc..f793e05db 100644 --- a/vid-webpack-master/src/app/shared/utils/constants.ts +++ b/vid-webpack-master/src/app/shared/utils/constants.ts @@ -92,7 +92,9 @@ export module Constants { public static WELCOME_PATH = 'welcome.htm'; public static IS_PERMITTED_SUB_PATH = '&isPermitted='; public static SERVICES_JOB_INFO_PATH = '../../asyncInstantiation'; + public static SERVICE_MODEL_ID = 'serviceModelId'; public static SERVICES_RETRY_TOPOLOGY = '../../asyncInstantiation/bulkForRetry'; + public static INSTANTIATION_TEMPLATE_TOPOLOGY = '../../asyncInstantiation/templateTopology'; public static CONFIGURATION_PATH = '../../get_property/{name}/defaultvalue'; public static SERVICES_JOB_AUDIT_PATH = '/auditStatus'; public static SERVICES_PROBE_PATH = "../../probe"; @@ -295,4 +297,8 @@ export module Constants { export class LegacyRegion { public static MEGA_REGION = ['AAIAIC25']; } + + export class ModelInfo { + public static UNLIMITED_DEFAULT = 'Unlimited (default)'; + } } diff --git a/vid-webpack-master/src/app/shared/utils/util.spec.ts b/vid-webpack-master/src/app/shared/utils/util.spec.ts index 2f9142f9c..ae39238c2 100644 --- a/vid-webpack-master/src/app/shared/utils/util.spec.ts +++ b/vid-webpack-master/src/app/shared/utils/util.spec.ts @@ -1,23 +1,8 @@ import {Utils} from "./utils"; -import {TestBed} from "@angular/core/testing"; +import each from "jest-each"; describe('Util', () => { - let util: Utils; - - beforeAll(done => (async () => { - TestBed.configureTestingModule({ - - }); - await TestBed.compileComponents(); - - util = new Utils(); - - })().then(done).catch(done.fail)); - - test('should be defined', () => { - expect(util).toBeDefined(); - }); test('hasContents should return false if object is undefined or null or empty', () => { expect(Utils.hasContents(undefined)).toBeFalsy(); @@ -28,4 +13,39 @@ describe('Util', () => { test('hasContents should return true if object is not undefined and not null and not empty', () => { expect(Utils.hasContents("someValue")).toBeTruthy(); }); + + const instantiationTypesDataProvider = [ + ['Macro', false ], + ['ALaCarte', true ], + ['ClientConfig', true], + ['dont know', true] + ]; + each(instantiationTypesDataProvider).test('instantiationType %s isALaCarte shall be %s', (instantiationType, expected ) => { + expect(Utils.isALaCarte(instantiationType)).toEqual(expected); + }); + + each([ + ["empty properties, empty flags",{}, {}, 1], + ["null properties, undefined flags",null, undefined, 1], + ["max_instances 3, flag is on", {max_instances:3}, {FLAG_2002_UNLIMITED_MAX: true}, 3], + ["max_instances 3, flag is off", {max_instances:3}, {FLAG_2002_UNLIMITED_MAX: false}, 3], + ["null properties, flag is on", null, {FLAG_2002_UNLIMITED_MAX: true}, null], + ["null properties, flag is off", null, {FLAG_2002_UNLIMITED_MAX: false}, 1], + ["undefined properties, flag is off", undefined, {FLAG_2002_UNLIMITED_MAX: false}, 1], + ]).test('getMaxFirstLevel %s', (desc, properties, flags, expected) => { + expect(Utils.getMaxFirstLevel(properties, flags)).toEqual(expected); + }); + + each([ + ["empty properties, empty flags",{}, {}, 1], + ["null properties, undefined flags",null, undefined, 1], + ["wrong field, flag is on", {max_instances:3}, {FLAG_2002_UNLIMITED_MAX: true}, null], + ["maxCountInstances 3, flag is on", {maxCountInstances:3}, {FLAG_2002_UNLIMITED_MAX: true}, 3], + ["maxCountInstances 3, flag is off", {maxCountInstances:3}, {FLAG_2002_UNLIMITED_MAX: true}, 3], + ]).test('getMaxFirstLevel %s', (desc, properties, flags, expected) => { + expect(Utils.getMaxVfModule(properties, flags)).toEqual(expected); + }); + + + }); diff --git a/vid-webpack-master/src/app/shared/utils/utils.ts b/vid-webpack-master/src/app/shared/utils/utils.ts index d63a3c997..3db770738 100644 --- a/vid-webpack-master/src/app/shared/utils/utils.ts +++ b/vid-webpack-master/src/app/shared/utils/utils.ts @@ -2,6 +2,21 @@ import * as _ from 'lodash' export class Utils { + static getMaxFirstLevel(properties, flags: { [key: string]: boolean }) : number | null{ + return this.getMaxInstancesAllowed(properties, 'max_instances', flags) + } + + static getMaxVfModule(properties, flags: { [key: string]: boolean }) : number | null{ + return this.getMaxInstancesAllowed(properties, 'maxCountInstances', flags) + } + + static getMaxInstancesAllowed(properties, filedName: string, flags: { [key: string]: boolean }) : number | null{ + if (!_.isNil(properties) && !_.isNil(properties[filedName])) { + return properties[filedName]; + } + return (flags && !!flags['FLAG_2002_UNLIMITED_MAX']) ? null : 1; + } + public static clampNumber = (number, min, max) => { return Math.max(min, Math.min(number, max)); }; @@ -179,6 +194,10 @@ export class Utils { return (convertedAsdcModel); }; + public static isALaCarte(instantiationType) { + return instantiationType !== 'Macro'; + } + private static convertOldModel(serviceModel ) { let resource = {}; let convertedAsdcModel = { |