2 * ============LICENSE_START=======================================================
4 * ================================================================================
5 * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
6 * ================================================================================
7 * Licensed under the Apache License, Version 2.0 (the "License");
8 * you may not use this file except in compliance with the License.
9 * You may obtain a copy of the License at
11 * http://www.apache.org/licenses/LICENSE-2.0
13 * Unless required by applicable law or agreed to in writing, software
14 * distributed under the License is distributed on an "AS IS" BASIS,
15 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 * See the License for the specific language governing permissions and
17 * limitations under the License.
18 * ============LICENSE_END=========================================================
21 import * as _ from "lodash";
22 import { Component, ViewChild, Inject, TemplateRef } from "@angular/core";
23 import { PropertiesService } from "../../services/properties.service";
24 import { PropertyFEModel, InstanceFePropertiesMap, InstanceBePropertiesMap, InstancePropertiesAPIMap, Component as ComponentData, FilterPropertiesAssignmentData, ModalModel, ButtonModel } from "app/models";
25 import { ResourceType } from "app/utils";
26 import { ComponentServiceNg2 } from "../../services/component-services/component.service";
27 import { TopologyTemplateService } from "../../services/component-services/topology-template.service";
28 import { ComponentInstanceServiceNg2 } from "../../services/component-instance-services/component-instance.service"
29 import { InputBEModel, InputFEModel, ComponentInstance, GroupInstance, PolicyInstance, PropertyBEModel, DerivedFEProperty, SimpleFlatProperty } from "app/models";
30 import { KeysPipe } from 'app/ng2/pipes/keys.pipe';
31 import { WorkspaceMode, EVENTS, PROPERTY_TYPES } from "../../../utils/constants";
32 import { EventListenerService } from "app/services/event-listener-service"
33 import { HierarchyDisplayOptions } from "../../components/logic/hierarchy-navigtion/hierarchy-display-options";
34 import { FilterPropertiesAssignmentComponent } from "../../components/logic/filter-properties-assignment/filter-properties-assignment.component";
35 import { PropertyRowSelectedEvent } from "../../components/logic/properties-table/properties-table.component";
36 import { HierarchyNavService } from "./services/hierarchy-nav.service";
37 import { PropertiesUtils } from "./services/properties.utils";
38 import { ComponentModeService } from "../../services/component-services/component-mode.service";
39 import { Tabs, Tab } from "../../components/ui/tabs/tabs.component";
40 import { InputsUtils } from "./services/inputs.utils";
41 import { InstanceFeDetails } from "../../../models/instance-fe-details";
42 import { SdcUiServices, SdcUiCommon } from "onap-ui-angular";
43 import { UnsavedChangesComponent } from "app/ng2/components/ui/forms/unsaved-changes/unsaved-changes.component";
44 import {PropertyCreatorComponent} from "./property-creator/property-creator.component";
45 import {ModalService} from "../../services/modal.service";
46 import { DeclareListComponent } from "./declare-list/declare-list.component";
47 import { CapabilitiesGroup, Capability } from "../../../models/capability";
48 import { ToscaPresentationData } from "../../../models/tosca-presentation";
49 import { Observable } from "rxjs";
51 const SERVICE_SELF_TITLE = "SELF";
53 templateUrl: './properties-assignment.page.component.html',
54 styleUrls: ['./properties-assignment.page.component.less']
56 export class PropertiesAssignmentComponent {
57 title = "Properties & Inputs";
59 component: ComponentData;
60 componentInstanceNamesMap: Map<string, InstanceFeDetails> = new Map<string, InstanceFeDetails>();//instanceUniqueId, {name, iconClass}
62 propertiesNavigationData = [];
63 instancesNavigationData = [];
65 instanceFePropertiesMap:InstanceFePropertiesMap;
66 inputs: Array<InputFEModel> = [];
67 policies: Array<PolicyInstance> = [];
68 instances: Array<ComponentInstance|GroupInstance|PolicyInstance> = [];
70 propertyStructureHeader: string;
72 selectedFlatProperty: SimpleFlatProperty = new SimpleFlatProperty();
73 selectedInstanceData: ComponentInstance|GroupInstance|PolicyInstance = null;
74 checkedPropertiesCount: number = 0;
75 checkedChildPropertiesCount: number = 0;
77 hierarchyPropertiesDisplayOptions:HierarchyDisplayOptions = new HierarchyDisplayOptions('path', 'name', 'childrens');
78 hierarchyInstancesDisplayOptions:HierarchyDisplayOptions = new HierarchyDisplayOptions('uniqueId', 'name', 'archived', null, 'iconClass');
79 displayClearSearch = false;
80 searchPropertyName:string;
82 isInputsTabSelected:boolean;
83 isPropertiesTabSelected:boolean;
84 isPoliciesTabSelected:boolean;
86 resourceIsReadonly:boolean;
87 loadingInstances:boolean = false;
88 loadingInputs:boolean = false;
89 loadingPolicies:boolean = false;
90 loadingProperties:boolean = false;
91 changedData:Array<PropertyFEModel|InputFEModel>;
92 hasChangedData:boolean;
93 isValidChangedData:boolean;
94 savingChangedData:boolean;
95 stateChangeStartUnregister:Function;
96 serviceBePropertiesMap: InstanceBePropertiesMap;
97 serviceBeCapabilitiesPropertiesMap: InstanceBePropertiesMap;
98 selectedInstance_FlattenCapabilitiesList: Capability[];
100 @ViewChild('hierarchyNavTabs') hierarchyNavTabs: Tabs;
101 @ViewChild('propertyInputTabs') propertyInputTabs: Tabs;
102 @ViewChild('advanceSearch') advanceSearch: FilterPropertiesAssignmentComponent;
104 constructor(private propertiesService: PropertiesService,
105 private hierarchyNavService: HierarchyNavService,
106 private propertiesUtils:PropertiesUtils,
107 private inputsUtils:InputsUtils,
108 private componentServiceNg2:ComponentServiceNg2,
109 private componentInstanceServiceNg2:ComponentInstanceServiceNg2,
110 @Inject("$stateParams") _stateParams,
111 @Inject("$scope") private $scope:ng.IScope,
112 @Inject("$state") private $state:ng.ui.IStateService,
113 @Inject("Notification") private Notification:any,
114 private componentModeService:ComponentModeService,
115 private EventListenerService:EventListenerService,
116 private ModalServiceSdcUI: SdcUiServices.ModalService,
117 private ModalService: ModalService,
118 private keysPipe:KeysPipe,
119 private topologyTemplateService: TopologyTemplateService) {
121 this.instanceFePropertiesMap = new InstanceFePropertiesMap();
122 /* This is the way you can access the component data, please do not use any data except metadata, all other data should be received from the new api calls on the first time
123 than if the data is already exist, no need to call the api again - Ask orit if you have any questions*/
124 this.component = _stateParams.component;
125 this.EventListenerService.registerObserverCallback(EVENTS.ON_LIFECYCLE_CHANGE, this.onCheckout);
126 this.updateViewMode();
128 this.changedData = [];
129 this.updateHasChangedData();
130 this.isValidChangedData = true;
134 console.log("==>" + this.constructor.name + ": ngOnInit");
135 this.loadingInputs = true;
136 this.loadingPolicies = true;
137 this.loadingInstances = true;
138 this.loadingProperties = true;
139 this.topologyTemplateService
140 .getComponentInputsWithProperties(this.component.componentType, this.component.uniqueId)
141 .subscribe(response => {
142 _.forEach(response.inputs, (input: InputBEModel) => {
143 const newInput: InputFEModel = new InputFEModel(input);
144 this.inputsUtils.resetInputDefaultValue(newInput, input.defaultValue);
145 this.inputs.push(newInput); //only push items that were declared via SDC
147 this.loadingInputs = false;
149 }, error => {}); //ignore error
150 this.componentServiceNg2
151 .getComponentResourcePropertiesData(this.component)
152 .subscribe(response => {
153 this.loadingPolicies = false;
155 this.instances.push(...response.componentInstances);
156 this.instances.push(...response.groupInstances);
157 this.instances.push(...response.policies);
159 _.forEach(response.policies, (policy: any) => {
160 const newPolicy: InputFEModel = new InputFEModel(policy);
161 this.inputsUtils.resetInputDefaultValue(newPolicy, policy.defaultValue);
162 this.policies.push(policy);
165 // add the service self instance to the top of the list.
166 const serviceInstance = new ComponentInstance();
167 serviceInstance.name = SERVICE_SELF_TITLE;
168 serviceInstance.uniqueId = this.component.uniqueId;
169 this.instances.unshift(serviceInstance);
171 _.forEach(this.instances, (instance) => {
172 this.instancesNavigationData.push(instance);
173 this.componentInstanceNamesMap[instance.uniqueId] = <InstanceFeDetails>{name: instance.name, iconClass:instance.iconClass, originArchived:instance.originArchived};
175 this.loadingInstances = false;
176 if (this.instancesNavigationData[0] == undefined) {
177 this.loadingProperties = false;
179 this.selectFirstInstanceByDefault();
180 }, error => { this.loadingInstances = false; }); //ignore error
182 this.stateChangeStartUnregister = this.$scope.$on('$stateChangeStart', (event, toState, toParams) => {
183 // stop if has changed properties
184 if (this.hasChangedData) {
185 event.preventDefault();
186 this.showUnsavedChangesAlert().then(() => {
187 this.$state.go(toState, toParams);
194 this.EventListenerService.unRegisterObserver(EVENTS.ON_LIFECYCLE_CHANGE);
195 this.stateChangeStartUnregister();
198 selectFirstInstanceByDefault = () => {
199 if (this.instancesNavigationData[0] !== undefined) {
200 this.onInstanceSelectedUpdate(this.instancesNavigationData[0]);
204 updateViewMode = () => {
205 this.isReadonly = this.componentModeService.getComponentMode(this.component) === WorkspaceMode.VIEW;
208 onCheckout = (component:ComponentData) => {
209 this.component = component;
210 this.updateViewMode();
213 isSelf = ():boolean => {
214 return this.selectedInstanceData && this.selectedInstanceData.uniqueId == this.component.uniqueId;
217 getServiceProperties(){
218 this.loadingProperties = false;
219 this.topologyTemplateService
220 .getServiceProperties(this.component.uniqueId)
221 .subscribe((response) => {
222 this.serviceBePropertiesMap = new InstanceBePropertiesMap();
223 this.serviceBePropertiesMap[this.component.uniqueId] = response;
224 this.processInstancePropertiesResponse(this.serviceBePropertiesMap, false);
225 this.loadingProperties = false;
227 this.loadingProperties = false;
231 onInstanceSelectedUpdate = (instance: ComponentInstance|GroupInstance|PolicyInstance) => {
232 // stop if has changed properties
233 if (this.hasChangedData) {
234 this.showUnsavedChangesAlert().then((resolve)=> {
235 this.changeSelectedInstance(instance)
240 this.changeSelectedInstance(instance);
243 changeSelectedInstance = (instance: ComponentInstance|GroupInstance|PolicyInstance) => {
244 this.selectedInstanceData = instance;
245 this.loadingProperties = true;
246 if (instance instanceof ComponentInstance) {
247 let instanceBePropertiesMap: InstanceBePropertiesMap = new InstanceBePropertiesMap();
248 if (this.isInput(instance.originType)) {
249 this.componentInstanceServiceNg2
250 .getComponentInstanceInputs(this.component, instance)
251 .subscribe(response => {
252 instanceBePropertiesMap[instance.uniqueId] = response;
253 this.processInstancePropertiesResponse(instanceBePropertiesMap, true);
254 this.loadingProperties = false;
257 } else if (this.isSelf()) {
258 this.getServiceProperties();
260 this.componentInstanceServiceNg2
261 .getComponentInstanceProperties(this.component, instance.uniqueId)
262 .subscribe(response => {
263 instanceBePropertiesMap[instance.uniqueId] = response;
264 this.processInstancePropertiesResponse(instanceBePropertiesMap, false);
265 this.loadingProperties = false;
270 this.resourceIsReadonly = (instance.componentName === "vnfConfiguration");
271 } else if (instance instanceof GroupInstance) {
272 let instanceBePropertiesMap: InstanceBePropertiesMap = new InstanceBePropertiesMap();
273 this.componentInstanceServiceNg2
274 .getComponentGroupInstanceProperties(this.component, this.selectedInstanceData.uniqueId)
275 .subscribe((response) => {
276 instanceBePropertiesMap[instance.uniqueId] = response;
277 this.processInstancePropertiesResponse(instanceBePropertiesMap, false);
278 this.loadingProperties = false;
280 } else if (instance instanceof PolicyInstance) {
281 let instanceBePropertiesMap: InstanceBePropertiesMap = new InstanceBePropertiesMap();
282 this.componentInstanceServiceNg2
283 .getComponentPolicyInstanceProperties(this.component.componentType, this.component.uniqueId, this.selectedInstanceData.uniqueId)
284 .subscribe((response) => {
285 instanceBePropertiesMap[instance.uniqueId] = response;
286 this.processInstancePropertiesResponse(instanceBePropertiesMap, false);
287 this.loadingProperties = false;
290 this.loadingProperties = false;
293 if (this.searchPropertyName) {
296 //clear selected property from the navigation
297 this.selectedFlatProperty = new SimpleFlatProperty();
298 this.propertiesNavigationData = [];
302 * Entry point handling response from server
304 processInstancePropertiesResponse = (instanceBePropertiesMap: InstanceBePropertiesMap, originTypeIsVF: boolean) => {
305 this.instanceFePropertiesMap = this.propertiesUtils.convertPropertiesMapToFEAndCreateChildren(instanceBePropertiesMap, originTypeIsVF, this.inputs); //create flattened children, disable declared props, and init values
306 this.checkedPropertiesCount = 0;
307 this.checkedChildPropertiesCount = 0;
310 processInstanceCapabilitiesPropertiesResponse = (originTypeIsVF: boolean) => {
311 let selectedComponentInstanceData = <ComponentInstance>(this.selectedInstanceData);
312 let currentUniqueId = this.selectedInstanceData.uniqueId;
313 this.serviceBeCapabilitiesPropertiesMap = new InstanceBePropertiesMap();
314 let isCapabilityOwnedByInstance: boolean;
315 this.serviceBeCapabilitiesPropertiesMap[currentUniqueId] = _.reduce(
316 this.selectedInstance_FlattenCapabilitiesList,
317 (result, cap: Capability) => {
318 isCapabilityOwnedByInstance = cap.ownerId === currentUniqueId ||
319 selectedComponentInstanceData.isServiceProxy() && cap.ownerId === selectedComponentInstanceData.sourceModelUid;
320 if (cap.properties && isCapabilityOwnedByInstance) {
321 _.forEach(cap.properties, prop => {
322 if (!prop.origName) {
323 prop.origName = prop.name;
324 prop.name = cap.name + '_' + prop.name;//for display. (before save - the name returns to its orig value: prop.name)
327 return result.concat(cap.properties);
331 let instanceFECapabilitiesPropertiesMap = this.propertiesUtils.convertPropertiesMapToFEAndCreateChildren(this.serviceBeCapabilitiesPropertiesMap, originTypeIsVF, this.inputs); //create flattened children, disable declared props, and init values
332 //update FECapabilitiesProperties with their origName according to BeCapabilitiesProperties
333 _.forEach(instanceFECapabilitiesPropertiesMap[currentUniqueId], prop => {
334 prop.origName = _.find(this.serviceBeCapabilitiesPropertiesMap[currentUniqueId], p => p.uniqueId === prop.uniqueId).origName;
336 //concatenate capabilitiesProps to all props list
337 this.instanceFePropertiesMap[currentUniqueId] = (this.instanceFePropertiesMap[currentUniqueId] || []).concat(instanceFECapabilitiesPropertiesMap[currentUniqueId]);
338 this.checkedPropertiesCount = 0;
341 isCapabilityProperty = (prop: PropertyBEModel) => {
342 return _.find(this.selectedInstance_FlattenCapabilitiesList, cap => cap.uniqueId === prop.parentUniqueId);
345 /*** VALUE CHANGE EVENTS ***/
346 dataChanged = (item:PropertyFEModel|InputFEModel) => {
348 if (this.isPropertiesTabSelected && item instanceof PropertyFEModel) {
349 itemHasChanged = item.hasValueObjChanged();
350 } else if (this.isInputsTabSelected && item instanceof InputFEModel) {
351 itemHasChanged = item.hasDefaultValueChanged();
352 } else if (this.isPoliciesTabSelected && item instanceof InputFEModel) {
353 itemHasChanged = item.hasDefaultValueChanged();
356 const dataChangedIdx = this.changedData.findIndex((changedItem) => changedItem === item);
357 if (itemHasChanged) {
358 if (dataChangedIdx === -1) {
359 this.changedData.push(item);
362 if (dataChangedIdx !== -1) {
363 this.changedData.splice(dataChangedIdx, 1);
367 if (this.isPropertiesTabSelected) {
368 this.isValidChangedData = this.changedData.every((changedItem) => (<PropertyFEModel>changedItem).valueObjIsValid);
369 } else if (this.isInputsTabSelected || this.isPoliciesTabSelected) {
370 this.isValidChangedData = this.changedData.every((changedItem) => (<InputFEModel>changedItem).defaultValueObjIsValid);
372 this.updateHasChangedData();
376 /*** HEIRARCHY/NAV RELATED FUNCTIONS ***/
379 * Handle select node in navigation area, and select the row in table
381 onPropertySelectedUpdate = ($event) => {
382 console.log("==>" + this.constructor.name + ": onPropertySelectedUpdate");
383 this.selectedFlatProperty = $event;
384 let parentProperty:PropertyFEModel = this.propertiesService.getParentPropertyFEModelFromPath(this.instanceFePropertiesMap[this.selectedFlatProperty.instanceName], this.selectedFlatProperty.path);
385 parentProperty.expandedChildPropertyId = this.selectedFlatProperty.path;
389 * When user select row in table, this will prepare the hirarchy object for the tree.
391 selectPropertyRow = (propertyRowSelectedEvent:PropertyRowSelectedEvent) => {
392 console.log("==>" + this.constructor.name + ": selectPropertyRow " + propertyRowSelectedEvent.propertyModel.name);
393 let property = propertyRowSelectedEvent.propertyModel;
394 let instanceName = propertyRowSelectedEvent.instanceName;
395 this.propertyStructureHeader = null;
397 // Build hirarchy tree for the navigation and update propertiesNavigationData with it.
398 if (!(this.selectedInstanceData instanceof ComponentInstance) || this.selectedInstanceData.originType !== ResourceType.VF) {
399 let simpleFlatProperty:Array<SimpleFlatProperty>;
400 if (property instanceof PropertyFEModel) {
401 simpleFlatProperty = this.hierarchyNavService.getSimplePropertiesTree(property, instanceName);
402 } else if (property instanceof DerivedFEProperty) {
403 // Need to find parent PropertyFEModel
404 let parentPropertyFEModel:PropertyFEModel = _.find(this.instanceFePropertiesMap[instanceName], (tmpFeProperty):boolean => {
405 return property.propertiesName.indexOf(tmpFeProperty.name)===0;
407 simpleFlatProperty = this.hierarchyNavService.getSimplePropertiesTree(parentPropertyFEModel, instanceName);
409 this.propertiesNavigationData = simpleFlatProperty;
412 // Update the header in the navigation tree with property name.
413 this.propertyStructureHeader = (property.propertiesName.split('#'))[0];
415 // Set selected property in table
416 this.selectedFlatProperty = this.hierarchyNavService.createSimpleFlatProperty(property, instanceName);
417 this.hierarchyNavTabs.triggerTabChange('Property Structure');
421 selectInstanceRow = ($event) => {//get instance name
422 this.selectedInstanceData = _.find(this.instancesNavigationData, (instance:ComponentInstance) => {
423 return instance.name == $event;
425 this.hierarchyNavTabs.triggerTabChange('Composition');
428 tabChanged = (event) => {
429 // stop if has changed properties
430 if (this.hasChangedData) {
431 this.propertyInputTabs.triggerTabChange(this.currentMainTab.title);
432 this.showUnsavedChangesAlert().then((proceed) => {
433 this.propertyInputTabs.selectTab(this.propertyInputTabs.tabs.find((tab) => tab.title === event.title));
439 console.log("==>" + this.constructor.name + ": tabChanged " + event);
440 this.currentMainTab = this.propertyInputTabs.tabs.find((tab) => tab.title === event.title);
441 this.isPropertiesTabSelected = this.currentMainTab.title === "Properties";
442 this.isInputsTabSelected = this.currentMainTab.title === "Inputs";
443 this.isPoliciesTabSelected = this.currentMainTab.title === "Policies";
444 this.propertyStructureHeader = null;
445 this.searchQuery = '';
450 /*** DECLARE PROPERTIES/INPUTS ***/
451 declareProperties = (): void => {
452 console.log("==>" + this.constructor.name + ": declareProperties");
454 let selectedComponentInstancesProperties: InstanceBePropertiesMap = new InstanceBePropertiesMap();
455 let selectedGroupInstancesProperties: InstanceBePropertiesMap = new InstanceBePropertiesMap();
456 let selectedPolicyInstancesProperties: InstanceBePropertiesMap = new InstanceBePropertiesMap();
457 let selectedComponentInstancesInputs: InstanceBePropertiesMap = new InstanceBePropertiesMap();
458 let instancesIds = this.keysPipe.transform(this.instanceFePropertiesMap, []);
460 angular.forEach(instancesIds, (instanceId: string): void => {
461 let selectedInstanceData: any = this.instances.find(instance => instance.uniqueId == instanceId);
462 if (selectedInstanceData instanceof ComponentInstance) {
463 if (!this.isInput(selectedInstanceData.originType)) {
464 // convert Property FE model -> Property BE model, extract only checked
465 selectedComponentInstancesProperties[instanceId] = this.propertiesService.getCheckedProperties(this.instanceFePropertiesMap[instanceId]);
467 selectedComponentInstancesInputs[instanceId] = this.propertiesService.getCheckedProperties(this.instanceFePropertiesMap[instanceId]);
469 } else if (selectedInstanceData instanceof GroupInstance) {
470 selectedGroupInstancesProperties[instanceId] = this.propertiesService.getCheckedProperties(this.instanceFePropertiesMap[instanceId]);
471 } else if (selectedInstanceData instanceof PolicyInstance) {
472 selectedPolicyInstancesProperties[instanceId] = this.propertiesService.getCheckedProperties(this.instanceFePropertiesMap[instanceId]);
476 let inputsToCreate: InstancePropertiesAPIMap = new InstancePropertiesAPIMap(selectedComponentInstancesInputs, selectedComponentInstancesProperties, selectedGroupInstancesProperties, selectedPolicyInstancesProperties);
478 //move changed capabilities properties from componentInstanceInputsMap obj to componentInstanceProperties
479 inputsToCreate.componentInstanceProperties[this.selectedInstanceData.uniqueId] =
480 (inputsToCreate.componentInstanceProperties[this.selectedInstanceData.uniqueId] || []).concat(
482 inputsToCreate.componentInstanceInputsMap[this.selectedInstanceData.uniqueId],
483 (prop: PropertyBEModel) => this.isCapabilityProperty(prop)
486 inputsToCreate.componentInstanceInputsMap[this.selectedInstanceData.uniqueId] = _.filter(
487 inputsToCreate.componentInstanceInputsMap[this.selectedInstanceData.uniqueId],
488 prop => !this.isCapabilityProperty(prop)
490 if (inputsToCreate.componentInstanceInputsMap[this.selectedInstanceData.uniqueId].length === 0) {
491 delete inputsToCreate.componentInstanceInputsMap[this.selectedInstanceData.uniqueId];
494 let isCapabilityPropertyChanged = false;
496 inputsToCreate.componentInstanceProperties[this.selectedInstanceData.uniqueId],
497 (prop: PropertyBEModel) => {
498 prop.name = prop.origName || prop.name;
499 if (this.isCapabilityProperty(prop)) {
500 isCapabilityPropertyChanged = true;
504 this.topologyTemplateService
505 .createInput(this.component, inputsToCreate, this.isSelf())
506 .subscribe((response) => {
507 this.setInputTabIndication(response.length);
508 this.checkedPropertiesCount = 0;
509 this.checkedChildPropertiesCount = 0;
510 _.forEach(response, (input: InputBEModel) => {
511 const newInput: InputFEModel = new InputFEModel(input);
512 this.inputsUtils.resetInputDefaultValue(newInput, input.defaultValue);
513 this.inputs.push(newInput);
514 this.updatePropertyValueAfterDeclare(newInput);
516 if (isCapabilityPropertyChanged) {
517 this.reloadInstanceCapabilities();
519 }, error => {}); //ignore error
522 declareListProperties = (): void => {
523 console.log('declareListProperties() - enter');
525 // get selected properties
526 let selectedComponentInstancesProperties: InstanceBePropertiesMap = new InstanceBePropertiesMap();
527 let selectedGroupInstancesProperties: InstanceBePropertiesMap = new InstanceBePropertiesMap();
528 let selectedPolicyInstancesProperties: InstanceBePropertiesMap = new InstanceBePropertiesMap();
529 let selectedComponentInstancesInputs: InstanceBePropertiesMap = new InstanceBePropertiesMap();
530 let instancesIds = new KeysPipe().transform(this.instanceFePropertiesMap, []);
531 let propertyNameList: Array<string> = [];
534 angular.forEach(instancesIds, (instanceId: string): void => {
535 console.log("instanceId="+instanceId);
537 let selectedInstanceData: any = this.instances.find(instance => instance.uniqueId == instanceId);
538 let checkedProperties: PropertyBEModel[] = this.propertiesService.getCheckedProperties(this.instanceFePropertiesMap[instanceId]);
540 if (selectedInstanceData instanceof ComponentInstance) {
541 if (!this.isInput(selectedInstanceData.originType)) {
542 // convert Property FE model -> Property BE model, extract only checked
543 selectedComponentInstancesProperties[instanceId] = checkedProperties;
545 selectedComponentInstancesInputs[instanceId] = checkedProperties;
547 } else if (selectedInstanceData instanceof GroupInstance) {
548 selectedGroupInstancesProperties[instanceId] = checkedProperties;
549 } else if (selectedInstanceData instanceof PolicyInstance) {
550 selectedPolicyInstancesProperties[instanceId] = checkedProperties;
553 angular.forEach(checkedProperties, (property: PropertyBEModel) => {
554 propertyNameList.push(property.name);
558 let inputsToCreate: InstancePropertiesAPIMap = new InstancePropertiesAPIMap(selectedComponentInstancesInputs, selectedComponentInstancesProperties, selectedGroupInstancesProperties, selectedPolicyInstancesProperties);
560 let modalTitle = 'Declare Properties as List Input';
561 const modal = this.ModalService.createCustomModal(new ModalModel(
563 modalTitle, /* title */
568 'blue', /* css class */
569 () => { /* callback */
570 let content:any = modal.instance.dynamicContent.instance;
573 let reglistInput: InstanceBePropertiesMap = new InstanceBePropertiesMap();
574 let typelist: any = PROPERTY_TYPES.LIST;
575 let uniID: any = insId;
576 let boolfalse: any = false;
580 "type": content.propertyModel.simpleType,
581 "required": boolfalse
584 let schemaProp :any = {
585 "type": content.propertyModel.simpleType,
586 "required": boolfalse
589 reglistInput.description = content.propertyModel.description;
590 reglistInput.name = content.propertyModel.name;
591 reglistInput.type = typelist;
592 reglistInput.schemaType = content.propertyModel.simpleType;
593 reglistInput.instanceUniqueId = uniID;
594 reglistInput.uniqueId = uniID;
595 reglistInput.required =boolfalse;
596 reglistInput.schema = schem;
597 reglistInput.schemaProperty = schemaProp;
600 componentInstInputsMap: content.inputsToCreate,
601 listInput: reglistInput
603 console.log("save button clicked. input=", input);
605 this.topologyTemplateService
606 .createListInput(this.component.uniqueId, input, this.isSelf())
607 .subscribe(response => {
608 this.setInputTabIndication(response.length);
609 this.checkedPropertiesCount = 0;
610 this.checkedChildPropertiesCount = 0;
611 _.forEach(response, (input: InputBEModel) => {
612 let newInput: InputFEModel = new InputFEModel(input);
613 this.inputsUtils.resetInputDefaultValue(newInput, input.defaultValue);
614 this.inputs.push(newInput);
615 // create list input does not return updated properties info, so need to reload
616 //this.updatePropertyValueAfterDeclare(newInput);
617 // Reload the whole instance for now - TODO: CHANGE THIS after the BE starts returning properties within the response, use commented code below instead!
618 this.changeSelectedInstance(this.selectedInstanceData);
620 modal.instance.close();
622 }, error => {}); //ignore error
625 /*, getDisabled: function */
627 new ButtonModel('Cancel', 'outline grey', () => {
628 modal.instance.close();
633 // 3rd arg is passed to DeclareListComponent instance
634 this.ModalService.addDynamicContentToModal(modal, DeclareListComponent, {properties: inputsToCreate, propertyNameList: propertyNameList});
635 modal.instance.open();
636 console.log('declareListProperties() - leave');
639 /*** DECLARE PROPERTIES/POLICIES ***/
640 declarePropertiesToPolicies = (): void => {
641 let selectedComponentInstancesProperties: InstanceBePropertiesMap = new InstanceBePropertiesMap();
642 let instancesIds = new KeysPipe().transform(this.instanceFePropertiesMap, []);
644 angular.forEach(instancesIds, (instanceId: string): void => {
645 let selectedInstanceData: any = this.instances.find(instance => instance.uniqueId == instanceId);
646 if (selectedInstanceData instanceof ComponentInstance) {
647 if (!this.isInput(selectedInstanceData.originType)) {
648 selectedComponentInstancesProperties[instanceId] = this.propertiesService.getCheckedProperties(this.instanceFePropertiesMap[instanceId]);
653 let policiesToCreate: InstancePropertiesAPIMap = new InstancePropertiesAPIMap(null, selectedComponentInstancesProperties, null, null);
654 this.loadingPolicies = true;
656 this.topologyTemplateService
657 .createPolicy(this.component, policiesToCreate, this.isSelf())
658 .subscribe(response => {
659 this.setPolicyTabIndication(response.length);
660 this.checkedPropertiesCount = 0;
661 this.displayPoliciesAsDeclared(response);
662 this.loadingPolicies = false;
667 displayPoliciesAsDeclared = (policies) => {
668 _.forEach(policies, (policy: any) => {
669 let newPolicy: InputFEModel = new InputFEModel(policy);
670 this.inputsUtils.resetInputDefaultValue(newPolicy, policy.defaultValue);
671 newPolicy.relatedPropertyName = policy.name;
672 newPolicy.relatedPropertyValue = policy.value;
673 this.updatePropertyValueAfterDeclare(newPolicy);
674 this.policies.push(policy);
678 saveChangedData = ():Promise<(PropertyBEModel|InputBEModel)[]> => {
679 return new Promise((resolve, reject) => {
680 if (!this.isValidChangedData) {
681 reject('Changed data is invalid - cannot save!');
684 if (!this.changedData.length) {
689 // make request and its handlers
691 let handleSuccess, handleError;
692 let changedInputsProperties = [], changedCapabilitiesProperties = [];
693 if (this.isPropertiesTabSelected) {
694 const changedProperties: PropertyBEModel[] = this.changedData.map((changedProp) => {
695 changedProp = <PropertyFEModel>changedProp;
696 const propBE = new PropertyBEModel(changedProp);
697 propBE.toscaPresentation = new ToscaPresentationData();
698 propBE.toscaPresentation.ownerId = changedProp.parentUniqueId;
699 propBE.value = changedProp.getJSONValue();
700 propBE.name = changedProp.origName || changedProp.name;
701 delete propBE.origName;
704 changedCapabilitiesProperties = _.filter(changedProperties, prop => this.isCapabilityProperty(prop));
706 if (this.selectedInstanceData instanceof ComponentInstance) {
707 if (this.isInput(this.selectedInstanceData.originType)) {
708 changedInputsProperties = _.filter(changedProperties, prop => !this.isCapabilityProperty(prop));
709 if (changedInputsProperties.length && changedCapabilitiesProperties.length) {
710 request = Observable.forkJoin(
711 this.componentInstanceServiceNg2.updateInstanceInputs(this.component, this.selectedInstanceData.uniqueId, changedInputsProperties),
712 this.componentInstanceServiceNg2.updateInstanceProperties(this.component.componentType, this.component.uniqueId,
713 this.selectedInstanceData.uniqueId, changedCapabilitiesProperties)
716 else if (changedInputsProperties.length) {
717 request = this.componentInstanceServiceNg2
718 .updateInstanceInputs(this.component, this.selectedInstanceData.uniqueId, changedInputsProperties);
720 else if (changedCapabilitiesProperties.length) {
721 request = this.componentInstanceServiceNg2
722 .updateInstanceProperties(this.component.componentType, this.component.uniqueId, this.selectedInstanceData.uniqueId, changedCapabilitiesProperties);
724 handleSuccess = (response) => {
725 // reset each changed property with new value and remove it from changed properties list
726 response.forEach((resInput) => {
727 const changedProp = <PropertyFEModel>this.changedData.shift();
728 this.propertiesUtils.resetPropertyValue(changedProp, resInput.value);
730 console.log('updated instance inputs:', response);
734 request = this.topologyTemplateService.updateServiceProperties(this.component.uniqueId, _.map(changedProperties, cp => {
735 delete cp.constraints;
739 request = this.componentInstanceServiceNg2
740 .updateInstanceProperties(this.component.componentType, this.component.uniqueId, this.selectedInstanceData.uniqueId, changedProperties);
742 handleSuccess = (response) => {
743 // reset each changed property with new value and remove it from changed properties list
744 response.forEach((resProp) => {
745 const changedProp = <PropertyFEModel>this.changedData.shift();
746 this.propertiesUtils.resetPropertyValue(changedProp, resProp.value);
749 console.log("updated instance properties: ", response);
752 } else if (this.selectedInstanceData instanceof GroupInstance) {
753 request = this.componentInstanceServiceNg2
754 .updateComponentGroupInstanceProperties(this.component.componentType, this.component.uniqueId, this.selectedInstanceData.uniqueId, changedProperties);
755 handleSuccess = (response) => {
756 // reset each changed property with new value and remove it from changed properties list
757 response.forEach((resProp) => {
758 const changedProp = <PropertyFEModel>this.changedData.shift();
759 this.propertiesUtils.resetPropertyValue(changedProp, resProp.value);
762 console.log("updated group instance properties: ", response);
764 } else if (this.selectedInstanceData instanceof PolicyInstance) {
765 request = this.componentInstanceServiceNg2
766 .updateComponentPolicyInstanceProperties(this.component.componentType, this.component.uniqueId, this.selectedInstanceData.uniqueId, changedProperties);
767 handleSuccess = (response) => {
768 // reset each changed property with new value and remove it from changed properties list
769 response.forEach((resProp) => {
770 const changedProp = <PropertyFEModel>this.changedData.shift();
771 this.propertiesUtils.resetPropertyValue(changedProp, resProp.value);
774 console.log("updated policy instance properties: ", response);
777 } else if (this.isInputsTabSelected) {
779 const changedInputs: InputBEModel[] = this.changedData.map((changedInput) => {
780 changedInput = <InputFEModel>changedInput;
781 const inputBE = new InputBEModel(changedInput);
782 inputBE.defaultValue = changedInput.getJSONDefaultValue();
785 request = this.componentServiceNg2
786 .updateComponentInputs(this.component, changedInputs);
787 handleSuccess = (response) => {
788 // reset each changed property with new value and remove it from changed properties list
789 response.forEach((resInput) => {
790 const changedInput = <InputFEModel>this.changedData.shift();
791 this.inputsUtils.resetInputDefaultValue(changedInput, resInput.defaultValue);
793 console.log("updated the component inputs and got this response: ", response);
797 this.savingChangedData = true;
800 this.savingChangedData = false;
801 if (changedCapabilitiesProperties.length) {
802 this.reloadInstanceCapabilities();
804 handleSuccess && handleSuccess(response);
805 this.updateHasChangedData();
809 this.savingChangedData = false;
810 handleError && handleError(error);
811 this.updateHasChangedData();
818 reloadInstanceCapabilities = (): void => {
819 let currentInstanceIndex = _.findIndex(this.instances, instance => instance.uniqueId == this.selectedInstanceData.uniqueId);
820 this.componentServiceNg2.getComponentResourceInstances(this.component).subscribe(result => {
821 let instanceCapabilitiesData: CapabilitiesGroup = _.reduce(result.componentInstances, (res, instance) => {
822 if (instance.uniqueId === this.selectedInstanceData.uniqueId) {
823 return instance.capabilities;
826 }, new CapabilitiesGroup());
827 (<ComponentInstance>this.instances[currentInstanceIndex]).capabilities = instanceCapabilitiesData;
831 reverseChangedData = ():void => {
832 // make reverse item handler
833 let handleReverseItem;
834 if (this.isPropertiesTabSelected) {
835 handleReverseItem = (changedItem) => {
836 changedItem = <PropertyFEModel>changedItem;
837 this.propertiesUtils.resetPropertyValue(changedItem, changedItem.value);
838 this.checkedPropertiesCount = 0;
839 this.checkedChildPropertiesCount = 0;
841 } else if (this.isInputsTabSelected) {
842 handleReverseItem = (changedItem) => {
843 changedItem = <InputFEModel>changedItem;
844 this.inputsUtils.resetInputDefaultValue(changedItem, changedItem.defaultValue);
848 this.changedData.forEach(handleReverseItem);
849 this.changedData = [];
850 this.updateHasChangedData();
853 updateHasChangedData = ():boolean => {
854 const curHasChangedData:boolean = (this.changedData.length > 0);
855 if (curHasChangedData !== this.hasChangedData) {
856 this.hasChangedData = curHasChangedData;
857 if(this.hasChangedData) {
858 this.EventListenerService.notifyObservers(EVENTS.ON_WORKSPACE_UNSAVED_CHANGES, this.hasChangedData, this.showUnsavedChangesAlert);
860 this.EventListenerService.notifyObservers(EVENTS.ON_WORKSPACE_UNSAVED_CHANGES, false);
863 return this.hasChangedData;
866 doSaveChangedData = (onSuccessFunction?:Function, onError?:Function):void => {
867 this.saveChangedData().then(
869 this.Notification.success({
870 message: 'Successfully saved changes',
873 if(onSuccessFunction) onSuccessFunction();
874 if(this.isPropertiesTabSelected){
875 this.checkedPropertiesCount = 0;
876 this.checkedChildPropertiesCount = 0;
880 this.Notification.error({
881 message: 'Failed to save changes!',
884 if(onError) onError();
889 showUnsavedChangesAlert = ():Promise<any> => {
890 let modalTitle:string;
891 if (this.isPropertiesTabSelected) {
892 modalTitle = `Unsaved properties for ${this.selectedInstanceData.name}`;
893 } else if (this.isInputsTabSelected) {
894 modalTitle = `Unsaved inputs for ${this.component.name}`;
897 return new Promise<any>((resolve, reject) => {
898 const modal = this.ModalServiceSdcUI.openCustomModal(
902 type: SdcUiCommon.ModalType.custom,
903 testId: "navigate-modal",
906 {id: 'cancelButton', text: 'Cancel', type: SdcUiCommon.ButtonType.secondary, size: 'xsm', closeModal: true, callback: () => reject()},
907 {id: 'discardButton', text: 'Discard', type: SdcUiCommon.ButtonType.secondary, size: 'xsm', closeModal: true, callback: () => { this.reverseChangedData(); resolve()}},
908 {id: 'saveButton', text: 'Save', type: SdcUiCommon.ButtonType.primary, size: 'xsm', closeModal: true, disabled: !this.isValidChangedData, callback: () => this.doSaveChangedData(resolve, reject)}
909 ] as SdcUiCommon.IModalButtonComponent[]
910 } as SdcUiCommon.IModalConfig, UnsavedChangesComponent, {isValidChangedData: this.isValidChangedData});
915 updatePropertyValueAfterDeclare = (input: InputFEModel) => {
916 if (this.instanceFePropertiesMap[input.instanceUniqueId]) {
917 const instanceName = input.instanceUniqueId.slice(input.instanceUniqueId.lastIndexOf('.') + 1);
918 const propertyForUpdatindVal = _.find(this.instanceFePropertiesMap[input.instanceUniqueId], (feProperty: PropertyFEModel) => {
919 return feProperty.name == input.relatedPropertyName &&
920 (feProperty.name == input.relatedPropertyName || input.name === instanceName.concat('_').concat(feProperty.name.replace(/[.]/g, '_')));
922 const inputPath = (input.inputPath && input.inputPath != propertyForUpdatindVal.name) ? input.inputPath : undefined;
923 propertyForUpdatindVal.setAsDeclared(inputPath); //set prop as declared before assigning value
924 this.propertiesService.disableRelatedProperties(propertyForUpdatindVal, inputPath);
925 this.propertiesUtils.resetPropertyValue(propertyForUpdatindVal, input.relatedPropertyValue, inputPath);
929 //used for declare button, to keep count of newly checked properties (and ignore declared properties)
930 updateCheckedPropertyCount = (increment: boolean): void => {
931 this.checkedPropertiesCount += (increment) ? 1 : -1;
932 console.log("CheckedProperties count is now.... " + this.checkedPropertiesCount);
935 updateCheckedChildPropertyCount = (increment: boolean): void => {
936 this.checkedChildPropertiesCount += (increment) ? 1 : -1;
939 setInputTabIndication = (numInputs: number): void => {
940 this.propertyInputTabs.setTabIndication('Inputs', numInputs);
943 setPolicyTabIndication = (numPolicies: number): void => {
944 this.propertyInputTabs.setTabIndication('Policies', numPolicies);
947 resetUnsavedChangesForInput = (input:InputFEModel) => {
948 this.inputsUtils.resetInputDefaultValue(input, input.defaultValue);
949 this.changedData = this.changedData.filter((changedItem) => changedItem.uniqueId !== input.uniqueId);
950 this.updateHasChangedData();
953 deleteInput = (input: InputFEModel) => {
954 //reset any unsaved changes to the input before deleting it
955 this.resetUnsavedChangesForInput(input);
957 console.log("==>" + this.constructor.name + ": deleteInput");
958 let inputToDelete = new InputBEModel(input);
960 this.componentServiceNg2
961 .deleteInput(this.component, inputToDelete)
962 .subscribe(response => {
963 this.inputs = this.inputs.filter(input => input.uniqueId !== response.uniqueId);
965 //Reload the whole instance for now - TODO: CHANGE THIS after the BE starts returning properties within the response, use commented code below instead!
966 this.changeSelectedInstance(this.selectedInstanceData);
967 // let instanceFeProperties = this.instanceFePropertiesMap[this.getInstanceUniqueId(input.instanceName)];
969 // if (instanceFeProperties) {
970 // let propToEnable: PropertyFEModel = instanceFeProperties.find((prop) => {
971 // return prop.name == input.propertyName;
974 // if (propToEnable) {
975 // if (propToEnable.name == response.inputPath) response.inputPath = null;
976 // propToEnable.setNonDeclared(response.inputPath);
977 // //this.propertiesUtils.resetPropertyValue(propToEnable, newValue, response.inputPath);
978 // this.propertiesService.undoDisableRelatedProperties(propToEnable, response.inputPath);
981 }, error => {}); //ignore error
984 deletePolicy = (policy: PolicyInstance) => {
985 this.loadingPolicies = true;
986 this.topologyTemplateService
987 .deletePolicy(this.component, policy)
988 .subscribe((response) => {
989 this.policies = this.policies.filter(policy => policy.uniqueId !== response.uniqueId);
990 //Reload the whole instance for now - TODO: CHANGE THIS after the BE starts returning properties within the response, use commented code below instead!
991 this.changeSelectedInstance(this.selectedInstanceData);
992 this.loadingPolicies = false;
996 deleteProperty = (property: PropertyFEModel) => {
997 const propertyToDelete = new PropertyFEModel(property);
998 this.loadingProperties = true;
999 const feMap = this.instanceFePropertiesMap;
1000 this.topologyTemplateService
1001 .deleteServiceProperty(this.component.uniqueId, propertyToDelete)
1002 .subscribe((response) => {
1003 const props = feMap[this.component.uniqueId];
1004 props.splice(props.findIndex(p => p.uniqueId === response),1);
1005 this.loadingProperties = false;
1007 this.loadingProperties = false;
1008 console.error(error);
1012 /*** addProperty ***/
1013 addProperty = () => {
1014 let modalTitle = 'Add Property';
1015 let modal = this.ModalService.createCustomModal(new ModalModel(
1020 new ButtonModel('Save', 'blue', () => {
1021 modal.instance.dynamicContent.instance.isLoading = true;
1022 const newProperty: PropertyBEModel = modal.instance.dynamicContent.instance.propertyModel;
1023 this.topologyTemplateService.createServiceProperty(this.component.uniqueId, newProperty)
1024 .subscribe((response) => {
1025 modal.instance.dynamicContent.instance.isLoading = false;
1026 const newProp: PropertyFEModel = this.propertiesUtils.convertAddPropertyBAToPropertyFE(response);
1027 this.instanceFePropertiesMap[this.component.uniqueId].push(newProp);
1028 modal.instance.close();
1030 modal.instance.dynamicContent.instance.isLoading = false;
1031 this.Notification.error({
1032 message: 'Failed to add property:' + error,
1036 }, () => !modal.instance.dynamicContent.instance.checkFormValidForSubmit()),
1037 new ButtonModel('Cancel', 'outline grey', () => {
1038 modal.instance.close();
1043 this.ModalService.addDynamicContentToModal(modal, PropertyCreatorComponent, {});
1044 modal.instance.open();
1047 /*** SEARCH RELATED FUNCTIONS ***/
1048 searchPropertiesInstances = (filterData:FilterPropertiesAssignmentData) => {
1049 let instanceBePropertiesMap:InstanceBePropertiesMap;
1050 this.componentServiceNg2
1051 .filterComponentInstanceProperties(this.component, filterData)
1052 .subscribe((response) => {
1053 this.processInstancePropertiesResponse(response, false);
1054 this.hierarchyPropertiesDisplayOptions.searchText = filterData.propertyName;//mark results in tree
1055 this.searchPropertyName = filterData.propertyName;//mark in table
1056 this.hierarchyNavTabs.triggerTabChange('Composition');
1057 this.propertiesNavigationData = [];
1058 this.displayClearSearch = true;
1059 }, (error) => {}); //ignore error
1063 clearSearch = () => {
1064 this.instancesNavigationData = this.instances;
1065 this.searchPropertyName = "";
1066 this.hierarchyPropertiesDisplayOptions.searchText = "";
1067 this.displayClearSearch = false;
1068 this.advanceSearch.clearAll();
1069 this.searchQuery = '';
1072 clickOnClearSearch = () => {
1074 this.selectFirstInstanceByDefault();
1075 this.hierarchyNavTabs.triggerTabChange('Composition');
1078 private isInput = (instanceType:string):boolean =>{
1079 return instanceType === ResourceType.VF || instanceType === ResourceType.PNF || instanceType === ResourceType.CVFC || instanceType === ResourceType.CR;