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() || selectedComponentInstanceData.isServiceSubstitution() &&
320 cap.ownerId === selectedComponentInstanceData.sourceModelUid;
321 if (cap.properties && isCapabilityOwnedByInstance) {
322 _.forEach(cap.properties, prop => {
323 if (!prop.origName) {
324 prop.origName = prop.name;
325 prop.name = cap.name + '_' + prop.name;//for display. (before save - the name returns to its orig value: prop.name)
328 return result.concat(cap.properties);
332 let instanceFECapabilitiesPropertiesMap = this.propertiesUtils.convertPropertiesMapToFEAndCreateChildren(this.serviceBeCapabilitiesPropertiesMap, originTypeIsVF, this.inputs); //create flattened children, disable declared props, and init values
333 //update FECapabilitiesProperties with their origName according to BeCapabilitiesProperties
334 _.forEach(instanceFECapabilitiesPropertiesMap[currentUniqueId], prop => {
335 prop.origName = _.find(this.serviceBeCapabilitiesPropertiesMap[currentUniqueId], p => p.uniqueId === prop.uniqueId).origName;
337 //concatenate capabilitiesProps to all props list
338 this.instanceFePropertiesMap[currentUniqueId] = (this.instanceFePropertiesMap[currentUniqueId] || []).concat(instanceFECapabilitiesPropertiesMap[currentUniqueId]);
339 this.checkedPropertiesCount = 0;
342 isCapabilityProperty = (prop: PropertyBEModel) => {
343 return _.find(this.selectedInstance_FlattenCapabilitiesList, cap => cap.uniqueId === prop.parentUniqueId);
346 /*** VALUE CHANGE EVENTS ***/
347 dataChanged = (item:PropertyFEModel|InputFEModel) => {
349 if (this.isPropertiesTabSelected && item instanceof PropertyFEModel) {
350 itemHasChanged = item.hasValueObjChanged();
351 } else if (this.isInputsTabSelected && item instanceof InputFEModel) {
352 itemHasChanged = item.hasChanged();
353 } else if (this.isPoliciesTabSelected && item instanceof InputFEModel) {
354 itemHasChanged = item.hasDefaultValueChanged();
357 const dataChangedIdx = this.changedData.findIndex((changedItem) => changedItem === item);
358 if (itemHasChanged) {
359 if (dataChangedIdx === -1) {
360 this.changedData.push(item);
363 if (dataChangedIdx !== -1) {
364 this.changedData.splice(dataChangedIdx, 1);
368 if (this.isPropertiesTabSelected) {
369 this.isValidChangedData = this.changedData.every((changedItem) => (<PropertyFEModel>changedItem).valueObjIsValid);
370 } else if (this.isInputsTabSelected || this.isPoliciesTabSelected) {
371 this.isValidChangedData = this.changedData.every((changedItem) => (<InputFEModel>changedItem).defaultValueObjIsValid);
373 this.updateHasChangedData();
377 /*** HEIRARCHY/NAV RELATED FUNCTIONS ***/
380 * Handle select node in navigation area, and select the row in table
382 onPropertySelectedUpdate = ($event) => {
383 console.log("==>" + this.constructor.name + ": onPropertySelectedUpdate");
384 this.selectedFlatProperty = $event;
385 let parentProperty:PropertyFEModel = this.propertiesService.getParentPropertyFEModelFromPath(this.instanceFePropertiesMap[this.selectedFlatProperty.instanceName], this.selectedFlatProperty.path);
386 parentProperty.expandedChildPropertyId = this.selectedFlatProperty.path;
390 * When user select row in table, this will prepare the hirarchy object for the tree.
392 selectPropertyRow = (propertyRowSelectedEvent:PropertyRowSelectedEvent) => {
393 console.log("==>" + this.constructor.name + ": selectPropertyRow " + propertyRowSelectedEvent.propertyModel.name);
394 let property = propertyRowSelectedEvent.propertyModel;
395 let instanceName = propertyRowSelectedEvent.instanceName;
396 this.propertyStructureHeader = null;
398 // Build hirarchy tree for the navigation and update propertiesNavigationData with it.
399 if (!(this.selectedInstanceData instanceof ComponentInstance) || this.selectedInstanceData.originType !== ResourceType.VF) {
400 let simpleFlatProperty:Array<SimpleFlatProperty>;
401 if (property instanceof PropertyFEModel) {
402 simpleFlatProperty = this.hierarchyNavService.getSimplePropertiesTree(property, instanceName);
403 } else if (property instanceof DerivedFEProperty) {
404 // Need to find parent PropertyFEModel
405 let parentPropertyFEModel:PropertyFEModel = _.find(this.instanceFePropertiesMap[instanceName], (tmpFeProperty):boolean => {
406 return property.propertiesName.indexOf(tmpFeProperty.name)===0;
408 simpleFlatProperty = this.hierarchyNavService.getSimplePropertiesTree(parentPropertyFEModel, instanceName);
410 this.propertiesNavigationData = simpleFlatProperty;
413 // Update the header in the navigation tree with property name.
414 this.propertyStructureHeader = (property.propertiesName.split('#'))[0];
416 // Set selected property in table
417 this.selectedFlatProperty = this.hierarchyNavService.createSimpleFlatProperty(property, instanceName);
418 this.hierarchyNavTabs.triggerTabChange('Property Structure');
422 selectInstanceRow = ($event) => {//get instance name
423 this.selectedInstanceData = _.find(this.instancesNavigationData, (instance:ComponentInstance) => {
424 return instance.name == $event;
426 this.hierarchyNavTabs.triggerTabChange('Composition');
429 tabChanged = (event) => {
430 // stop if has changed properties
431 if (this.hasChangedData) {
432 this.propertyInputTabs.triggerTabChange(this.currentMainTab.title);
433 this.showUnsavedChangesAlert().then((proceed) => {
434 this.propertyInputTabs.selectTab(this.propertyInputTabs.tabs.find((tab) => tab.title === event.title));
440 console.log("==>" + this.constructor.name + ": tabChanged " + event);
441 this.currentMainTab = this.propertyInputTabs.tabs.find((tab) => tab.title === event.title);
442 this.isPropertiesTabSelected = this.currentMainTab.title === "Properties";
443 this.isInputsTabSelected = this.currentMainTab.title === "Inputs";
444 this.isPoliciesTabSelected = this.currentMainTab.title === "Policies";
445 this.propertyStructureHeader = null;
446 this.searchQuery = '';
451 /*** DECLARE PROPERTIES/INPUTS ***/
452 declareProperties = (): void => {
453 console.log("==>" + this.constructor.name + ": declareProperties");
455 let selectedComponentInstancesProperties: InstanceBePropertiesMap = new InstanceBePropertiesMap();
456 let selectedGroupInstancesProperties: InstanceBePropertiesMap = new InstanceBePropertiesMap();
457 let selectedPolicyInstancesProperties: InstanceBePropertiesMap = new InstanceBePropertiesMap();
458 let selectedComponentInstancesInputs: InstanceBePropertiesMap = new InstanceBePropertiesMap();
459 let instancesIds = this.keysPipe.transform(this.instanceFePropertiesMap, []);
461 angular.forEach(instancesIds, (instanceId: string): void => {
462 let selectedInstanceData: any = this.instances.find(instance => instance.uniqueId == instanceId);
463 if (selectedInstanceData instanceof ComponentInstance) {
464 if (!this.isInput(selectedInstanceData.originType)) {
465 // convert Property FE model -> Property BE model, extract only checked
466 selectedComponentInstancesProperties[instanceId] = this.propertiesService.getCheckedProperties(this.instanceFePropertiesMap[instanceId]);
468 selectedComponentInstancesInputs[instanceId] = this.propertiesService.getCheckedProperties(this.instanceFePropertiesMap[instanceId]);
470 } else if (selectedInstanceData instanceof GroupInstance) {
471 selectedGroupInstancesProperties[instanceId] = this.propertiesService.getCheckedProperties(this.instanceFePropertiesMap[instanceId]);
472 } else if (selectedInstanceData instanceof PolicyInstance) {
473 selectedPolicyInstancesProperties[instanceId] = this.propertiesService.getCheckedProperties(this.instanceFePropertiesMap[instanceId]);
477 let inputsToCreate: InstancePropertiesAPIMap = new InstancePropertiesAPIMap(selectedComponentInstancesInputs, selectedComponentInstancesProperties, selectedGroupInstancesProperties, selectedPolicyInstancesProperties);
479 //move changed capabilities properties from componentInstanceInputsMap obj to componentInstanceProperties
480 inputsToCreate.componentInstanceProperties[this.selectedInstanceData.uniqueId] =
481 (inputsToCreate.componentInstanceProperties[this.selectedInstanceData.uniqueId] || []).concat(
483 inputsToCreate.componentInstanceInputsMap[this.selectedInstanceData.uniqueId],
484 (prop: PropertyBEModel) => this.isCapabilityProperty(prop)
487 inputsToCreate.componentInstanceInputsMap[this.selectedInstanceData.uniqueId] = _.filter(
488 inputsToCreate.componentInstanceInputsMap[this.selectedInstanceData.uniqueId],
489 prop => !this.isCapabilityProperty(prop)
491 if (inputsToCreate.componentInstanceInputsMap[this.selectedInstanceData.uniqueId].length === 0) {
492 delete inputsToCreate.componentInstanceInputsMap[this.selectedInstanceData.uniqueId];
495 let isCapabilityPropertyChanged = false;
497 inputsToCreate.componentInstanceProperties[this.selectedInstanceData.uniqueId],
498 (prop: PropertyBEModel) => {
499 prop.name = prop.origName || prop.name;
500 if (this.isCapabilityProperty(prop)) {
501 isCapabilityPropertyChanged = true;
505 this.topologyTemplateService
506 .createInput(this.component, inputsToCreate, this.isSelf())
507 .subscribe((response) => {
508 this.setInputTabIndication(response.length);
509 this.checkedPropertiesCount = 0;
510 this.checkedChildPropertiesCount = 0;
511 _.forEach(response, (input: InputBEModel) => {
512 const newInput: InputFEModel = new InputFEModel(input);
513 this.inputsUtils.resetInputDefaultValue(newInput, input.defaultValue);
514 this.inputs.push(newInput);
515 this.updatePropertyValueAfterDeclare(newInput);
517 if (isCapabilityPropertyChanged) {
518 this.reloadInstanceCapabilities();
520 }, error => {}); //ignore error
523 declareListProperties = (): void => {
524 console.log('declareListProperties() - enter');
526 // get selected properties
527 let selectedComponentInstancesProperties: InstanceBePropertiesMap = new InstanceBePropertiesMap();
528 let selectedGroupInstancesProperties: InstanceBePropertiesMap = new InstanceBePropertiesMap();
529 let selectedPolicyInstancesProperties: InstanceBePropertiesMap = new InstanceBePropertiesMap();
530 let selectedComponentInstancesInputs: InstanceBePropertiesMap = new InstanceBePropertiesMap();
531 let instancesIds = new KeysPipe().transform(this.instanceFePropertiesMap, []);
532 let propertyNameList: Array<string> = [];
535 angular.forEach(instancesIds, (instanceId: string): void => {
536 console.log("instanceId="+instanceId);
538 let selectedInstanceData: any = this.instances.find(instance => instance.uniqueId == instanceId);
539 let checkedProperties: PropertyBEModel[] = this.propertiesService.getCheckedProperties(this.instanceFePropertiesMap[instanceId]);
541 if (selectedInstanceData instanceof ComponentInstance) {
542 if (!this.isInput(selectedInstanceData.originType)) {
543 // convert Property FE model -> Property BE model, extract only checked
544 selectedComponentInstancesProperties[instanceId] = checkedProperties;
546 selectedComponentInstancesInputs[instanceId] = checkedProperties;
548 } else if (selectedInstanceData instanceof GroupInstance) {
549 selectedGroupInstancesProperties[instanceId] = checkedProperties;
550 } else if (selectedInstanceData instanceof PolicyInstance) {
551 selectedPolicyInstancesProperties[instanceId] = checkedProperties;
554 angular.forEach(checkedProperties, (property: PropertyBEModel) => {
555 propertyNameList.push(property.name);
559 let inputsToCreate: InstancePropertiesAPIMap = new InstancePropertiesAPIMap(selectedComponentInstancesInputs, selectedComponentInstancesProperties, selectedGroupInstancesProperties, selectedPolicyInstancesProperties);
561 let modalTitle = 'Declare Properties as List Input';
562 const modal = this.ModalService.createCustomModal(new ModalModel(
564 modalTitle, /* title */
569 'blue', /* css class */
570 () => { /* callback */
571 let content:any = modal.instance.dynamicContent.instance;
574 let reglistInput: InstanceBePropertiesMap = new InstanceBePropertiesMap();
575 let typelist: any = PROPERTY_TYPES.LIST;
576 let uniID: any = insId;
577 let boolfalse: any = false;
578 let required: any = content.propertyModel.required;
582 "type": content.propertyModel.simpleType,
586 let schemaProp :any = {
587 "type": content.propertyModel.simpleType,
591 reglistInput.description = content.propertyModel.description;
592 reglistInput.name = content.propertyModel.name;
593 reglistInput.type = typelist;
594 reglistInput.schemaType = content.propertyModel.simpleType;
595 reglistInput.instanceUniqueId = uniID;
596 reglistInput.uniqueId = uniID;
597 reglistInput.required = required;
598 reglistInput.schema = schem;
599 reglistInput.schemaProperty = schemaProp;
602 componentInstInputsMap: content.inputsToCreate,
603 listInput: reglistInput
605 console.log("save button clicked. input=", input);
607 this.topologyTemplateService
608 .createListInput(this.component.uniqueId, input, this.isSelf())
609 .subscribe(response => {
610 this.setInputTabIndication(response.length);
611 this.checkedPropertiesCount = 0;
612 this.checkedChildPropertiesCount = 0;
613 _.forEach(response, (input: InputBEModel) => {
614 let newInput: InputFEModel = new InputFEModel(input);
615 this.inputsUtils.resetInputDefaultValue(newInput, input.defaultValue);
616 this.inputs.push(newInput);
617 // create list input does not return updated properties info, so need to reload
618 //this.updatePropertyValueAfterDeclare(newInput);
619 // Reload the whole instance for now - TODO: CHANGE THIS after the BE starts returning properties within the response, use commented code below instead!
620 this.changeSelectedInstance(this.selectedInstanceData);
622 modal.instance.close();
624 }, error => {}); //ignore error
627 /*, getDisabled: function */
629 new ButtonModel('Cancel', 'outline grey', () => {
630 modal.instance.close();
635 // 3rd arg is passed to DeclareListComponent instance
636 this.ModalService.addDynamicContentToModal(modal, DeclareListComponent, {properties: inputsToCreate, propertyNameList: propertyNameList});
637 modal.instance.open();
638 console.log('declareListProperties() - leave');
641 /*** DECLARE PROPERTIES/POLICIES ***/
642 declarePropertiesToPolicies = (): void => {
643 let selectedComponentInstancesProperties: InstanceBePropertiesMap = new InstanceBePropertiesMap();
644 let instancesIds = new KeysPipe().transform(this.instanceFePropertiesMap, []);
646 angular.forEach(instancesIds, (instanceId: string): void => {
647 let selectedInstanceData: any = this.instances.find(instance => instance.uniqueId == instanceId);
648 if (selectedInstanceData instanceof ComponentInstance) {
649 if (!this.isInput(selectedInstanceData.originType)) {
650 selectedComponentInstancesProperties[instanceId] = this.propertiesService.getCheckedProperties(this.instanceFePropertiesMap[instanceId]);
655 let policiesToCreate: InstancePropertiesAPIMap = new InstancePropertiesAPIMap(null, selectedComponentInstancesProperties, null, null);
656 this.loadingPolicies = true;
658 this.topologyTemplateService
659 .createPolicy(this.component, policiesToCreate, this.isSelf())
660 .subscribe(response => {
661 this.setPolicyTabIndication(response.length);
662 this.checkedPropertiesCount = 0;
663 this.displayPoliciesAsDeclared(response);
664 this.loadingPolicies = false;
669 displayPoliciesAsDeclared = (policies) => {
670 _.forEach(policies, (policy: any) => {
671 let newPolicy: InputFEModel = new InputFEModel(policy);
672 this.inputsUtils.resetInputDefaultValue(newPolicy, policy.defaultValue);
673 newPolicy.relatedPropertyName = policy.name;
674 newPolicy.relatedPropertyValue = policy.value;
675 this.updatePropertyValueAfterDeclare(newPolicy);
676 this.policies.push(policy);
680 saveChangedData = ():Promise<(PropertyBEModel|InputBEModel)[]> => {
681 return new Promise((resolve, reject) => {
682 if (!this.isValidChangedData) {
683 reject('Changed data is invalid - cannot save!');
686 if (!this.changedData.length) {
691 // make request and its handlers
693 let handleSuccess, handleError;
694 let changedInputsProperties = [], changedCapabilitiesProperties = [];
695 if (this.isPropertiesTabSelected) {
696 const changedProperties: PropertyBEModel[] = this.changedData.map((changedProp) => {
697 changedProp = <PropertyFEModel>changedProp;
698 const propBE = new PropertyBEModel(changedProp);
699 propBE.toscaPresentation = new ToscaPresentationData();
700 propBE.toscaPresentation.ownerId = changedProp.parentUniqueId;
701 propBE.value = changedProp.getJSONValue();
702 propBE.name = changedProp.origName || changedProp.name;
703 delete propBE.origName;
706 changedCapabilitiesProperties = _.filter(changedProperties, prop => this.isCapabilityProperty(prop));
708 if (this.selectedInstanceData instanceof ComponentInstance) {
709 if (this.isInput(this.selectedInstanceData.originType)) {
710 changedInputsProperties = _.filter(changedProperties, prop => !this.isCapabilityProperty(prop));
711 if (changedInputsProperties.length && changedCapabilitiesProperties.length) {
712 request = Observable.forkJoin(
713 this.componentInstanceServiceNg2.updateInstanceInputs(this.component, this.selectedInstanceData.uniqueId, changedInputsProperties),
714 this.componentInstanceServiceNg2.updateInstanceProperties(this.component.componentType, this.component.uniqueId,
715 this.selectedInstanceData.uniqueId, changedCapabilitiesProperties)
718 else if (changedInputsProperties.length) {
719 request = this.componentInstanceServiceNg2
720 .updateInstanceInputs(this.component, this.selectedInstanceData.uniqueId, changedInputsProperties);
722 else if (changedCapabilitiesProperties.length) {
723 request = this.componentInstanceServiceNg2
724 .updateInstanceProperties(this.component.componentType, this.component.uniqueId, this.selectedInstanceData.uniqueId, changedCapabilitiesProperties);
726 handleSuccess = (response) => {
727 // reset each changed property with new value and remove it from changed properties list
728 response.forEach((resInput) => {
729 const changedProp = <PropertyFEModel>this.changedData.shift();
730 this.propertiesUtils.resetPropertyValue(changedProp, resInput.value);
732 console.log('updated instance inputs:', response);
736 request = this.topologyTemplateService.updateServiceProperties(this.component.uniqueId, _.map(changedProperties, cp => {
737 delete cp.constraints;
741 request = this.componentInstanceServiceNg2
742 .updateInstanceProperties(this.component.componentType, this.component.uniqueId, this.selectedInstanceData.uniqueId, changedProperties);
744 handleSuccess = (response) => {
745 // reset each changed property with new value and remove it from changed properties list
746 response.forEach((resProp) => {
747 const changedProp = <PropertyFEModel>this.changedData.shift();
748 this.propertiesUtils.resetPropertyValue(changedProp, resProp.value);
751 console.log("updated instance properties: ", response);
754 } else if (this.selectedInstanceData instanceof GroupInstance) {
755 request = this.componentInstanceServiceNg2
756 .updateComponentGroupInstanceProperties(this.component.componentType, this.component.uniqueId, this.selectedInstanceData.uniqueId, changedProperties);
757 handleSuccess = (response) => {
758 // reset each changed property with new value and remove it from changed properties list
759 response.forEach((resProp) => {
760 const changedProp = <PropertyFEModel>this.changedData.shift();
761 this.propertiesUtils.resetPropertyValue(changedProp, resProp.value);
764 console.log("updated group instance properties: ", response);
766 } else if (this.selectedInstanceData instanceof PolicyInstance) {
767 request = this.componentInstanceServiceNg2
768 .updateComponentPolicyInstanceProperties(this.component.componentType, this.component.uniqueId, this.selectedInstanceData.uniqueId, changedProperties);
769 handleSuccess = (response) => {
770 // reset each changed property with new value and remove it from changed properties list
771 response.forEach((resProp) => {
772 const changedProp = <PropertyFEModel>this.changedData.shift();
773 this.propertiesUtils.resetPropertyValue(changedProp, resProp.value);
776 console.log("updated policy instance properties: ", response);
779 } else if (this.isInputsTabSelected) {
781 const changedInputs: InputBEModel[] = this.changedData.map((changedInput) => {
782 changedInput = <InputFEModel>changedInput;
783 const inputBE = new InputBEModel(changedInput);
784 inputBE.defaultValue = changedInput.getJSONDefaultValue();
787 request = this.componentServiceNg2
788 .updateComponentInputs(this.component, changedInputs);
789 handleSuccess = (response) => {
790 // reset each changed property with new value and remove it from changed properties list
791 response.forEach((resInput) => {
792 const changedInput = <InputFEModel>this.changedData.shift();
793 this.inputsUtils.resetInputDefaultValue(changedInput, resInput.defaultValue);
794 changedInput.required = resInput.required;
795 changedInput.requiredOrig = resInput.required;
797 console.log("updated the component inputs and got this response: ", response);
801 this.savingChangedData = true;
804 this.savingChangedData = false;
805 if (changedCapabilitiesProperties.length) {
806 this.reloadInstanceCapabilities();
808 handleSuccess && handleSuccess(response);
809 this.updateHasChangedData();
813 this.savingChangedData = false;
814 handleError && handleError(error);
815 this.updateHasChangedData();
822 reloadInstanceCapabilities = (): void => {
823 let currentInstanceIndex = _.findIndex(this.instances, instance => instance.uniqueId == this.selectedInstanceData.uniqueId);
824 this.componentServiceNg2.getComponentResourceInstances(this.component).subscribe(result => {
825 let instanceCapabilitiesData: CapabilitiesGroup = _.reduce(result.componentInstances, (res, instance) => {
826 if (instance.uniqueId === this.selectedInstanceData.uniqueId) {
827 return instance.capabilities;
830 }, new CapabilitiesGroup());
831 (<ComponentInstance>this.instances[currentInstanceIndex]).capabilities = instanceCapabilitiesData;
835 reverseChangedData = ():void => {
836 // make reverse item handler
837 let handleReverseItem;
838 if (this.isPropertiesTabSelected) {
839 handleReverseItem = (changedItem) => {
840 changedItem = <PropertyFEModel>changedItem;
841 this.propertiesUtils.resetPropertyValue(changedItem, changedItem.value);
842 this.checkedPropertiesCount = 0;
843 this.checkedChildPropertiesCount = 0;
845 } else if (this.isInputsTabSelected) {
846 handleReverseItem = (changedItem) => {
847 changedItem = <InputFEModel>changedItem;
848 this.inputsUtils.resetInputDefaultValue(changedItem, changedItem.defaultValue);
849 changedItem.required = changedItem.requiredOrig;
853 this.changedData.forEach(handleReverseItem);
854 this.changedData = [];
855 this.updateHasChangedData();
858 updateHasChangedData = ():boolean => {
859 const curHasChangedData:boolean = (this.changedData.length > 0);
860 if (curHasChangedData !== this.hasChangedData) {
861 this.hasChangedData = curHasChangedData;
862 if(this.hasChangedData) {
863 this.EventListenerService.notifyObservers(EVENTS.ON_WORKSPACE_UNSAVED_CHANGES, this.hasChangedData, this.showUnsavedChangesAlert);
865 this.EventListenerService.notifyObservers(EVENTS.ON_WORKSPACE_UNSAVED_CHANGES, false);
868 return this.hasChangedData;
871 doSaveChangedData = (onSuccessFunction?:Function, onError?:Function):void => {
872 this.saveChangedData().then(
874 this.Notification.success({
875 message: 'Successfully saved changes',
878 if(onSuccessFunction) onSuccessFunction();
879 if(this.isPropertiesTabSelected){
880 this.checkedPropertiesCount = 0;
881 this.checkedChildPropertiesCount = 0;
885 this.Notification.error({
886 message: 'Failed to save changes!',
889 if(onError) onError();
894 showUnsavedChangesAlert = ():Promise<any> => {
895 let modalTitle:string;
896 if (this.isPropertiesTabSelected) {
897 modalTitle = `Unsaved properties for ${this.selectedInstanceData.name}`;
898 } else if (this.isInputsTabSelected) {
899 modalTitle = `Unsaved inputs for ${this.component.name}`;
902 return new Promise<any>((resolve, reject) => {
903 const modal = this.ModalServiceSdcUI.openCustomModal(
907 type: SdcUiCommon.ModalType.custom,
908 testId: "navigate-modal",
911 {id: 'cancelButton', text: 'Cancel', type: SdcUiCommon.ButtonType.secondary, size: 'xsm', closeModal: true, callback: () => reject()},
912 {id: 'discardButton', text: 'Discard', type: SdcUiCommon.ButtonType.secondary, size: 'xsm', closeModal: true, callback: () => { this.reverseChangedData(); resolve()}},
913 {id: 'saveButton', text: 'Save', type: SdcUiCommon.ButtonType.primary, size: 'xsm', closeModal: true, disabled: !this.isValidChangedData, callback: () => this.doSaveChangedData(resolve, reject)}
914 ] as SdcUiCommon.IModalButtonComponent[]
915 } as SdcUiCommon.IModalConfig, UnsavedChangesComponent, {isValidChangedData: this.isValidChangedData});
920 updatePropertyValueAfterDeclare = (input: InputFEModel) => {
921 if (this.instanceFePropertiesMap[input.instanceUniqueId]) {
922 const instanceName = input.instanceUniqueId.slice(input.instanceUniqueId.lastIndexOf('.') + 1);
923 const propertyForUpdatindVal = _.find(this.instanceFePropertiesMap[input.instanceUniqueId], (feProperty: PropertyFEModel) => {
924 return feProperty.name == input.relatedPropertyName &&
925 (feProperty.name == input.relatedPropertyName || input.name === instanceName.concat('_').concat(feProperty.name.replace(/[.]/g, '_')));
927 const inputPath = (input.inputPath && input.inputPath != propertyForUpdatindVal.name) ? input.inputPath : undefined;
928 propertyForUpdatindVal.setAsDeclared(inputPath); //set prop as declared before assigning value
929 this.propertiesService.disableRelatedProperties(propertyForUpdatindVal, inputPath);
930 this.propertiesUtils.resetPropertyValue(propertyForUpdatindVal, input.relatedPropertyValue, inputPath);
934 //used for declare button, to keep count of newly checked properties (and ignore declared properties)
935 updateCheckedPropertyCount = (increment: boolean): void => {
936 this.checkedPropertiesCount += (increment) ? 1 : -1;
937 console.log("CheckedProperties count is now.... " + this.checkedPropertiesCount);
940 updateCheckedChildPropertyCount = (increment: boolean): void => {
941 this.checkedChildPropertiesCount += (increment) ? 1 : -1;
944 setInputTabIndication = (numInputs: number): void => {
945 this.propertyInputTabs.setTabIndication('Inputs', numInputs);
948 setPolicyTabIndication = (numPolicies: number): void => {
949 this.propertyInputTabs.setTabIndication('Policies', numPolicies);
952 resetUnsavedChangesForInput = (input:InputFEModel) => {
953 this.inputsUtils.resetInputDefaultValue(input, input.defaultValue);
954 this.changedData = this.changedData.filter((changedItem) => changedItem.uniqueId !== input.uniqueId);
955 this.updateHasChangedData();
958 deleteInput = (input: InputFEModel) => {
959 //reset any unsaved changes to the input before deleting it
960 this.resetUnsavedChangesForInput(input);
962 console.log("==>" + this.constructor.name + ": deleteInput");
963 let inputToDelete = new InputBEModel(input);
965 this.componentServiceNg2
966 .deleteInput(this.component, inputToDelete)
967 .subscribe(response => {
968 this.inputs = this.inputs.filter(input => input.uniqueId !== response.uniqueId);
970 //Reload the whole instance for now - TODO: CHANGE THIS after the BE starts returning properties within the response, use commented code below instead!
971 this.changeSelectedInstance(this.selectedInstanceData);
972 // let instanceFeProperties = this.instanceFePropertiesMap[this.getInstanceUniqueId(input.instanceName)];
974 // if (instanceFeProperties) {
975 // let propToEnable: PropertyFEModel = instanceFeProperties.find((prop) => {
976 // return prop.name == input.propertyName;
979 // if (propToEnable) {
980 // if (propToEnable.name == response.inputPath) response.inputPath = null;
981 // propToEnable.setNonDeclared(response.inputPath);
982 // //this.propertiesUtils.resetPropertyValue(propToEnable, newValue, response.inputPath);
983 // this.propertiesService.undoDisableRelatedProperties(propToEnable, response.inputPath);
986 }, error => {}); //ignore error
989 deletePolicy = (policy: PolicyInstance) => {
990 this.loadingPolicies = true;
991 this.topologyTemplateService
992 .deletePolicy(this.component, policy)
993 .subscribe((response) => {
994 this.policies = this.policies.filter(policy => policy.uniqueId !== response.uniqueId);
995 //Reload the whole instance for now - TODO: CHANGE THIS after the BE starts returning properties within the response, use commented code below instead!
996 this.changeSelectedInstance(this.selectedInstanceData);
997 this.loadingPolicies = false;
1001 deleteProperty = (property: PropertyFEModel) => {
1002 const propertyToDelete = new PropertyFEModel(property);
1003 this.loadingProperties = true;
1004 const feMap = this.instanceFePropertiesMap;
1005 this.topologyTemplateService
1006 .deleteServiceProperty(this.component.uniqueId, propertyToDelete)
1007 .subscribe((response) => {
1008 const props = feMap[this.component.uniqueId];
1009 props.splice(props.findIndex(p => p.uniqueId === response),1);
1010 this.loadingProperties = false;
1012 this.loadingProperties = false;
1013 console.error(error);
1017 /*** addProperty ***/
1018 addProperty = () => {
1019 let modalTitle = 'Add Property';
1020 let modal = this.ModalService.createCustomModal(new ModalModel(
1025 new ButtonModel('Save', 'blue', () => {
1026 modal.instance.dynamicContent.instance.isLoading = true;
1027 const newProperty: PropertyBEModel = modal.instance.dynamicContent.instance.propertyModel;
1028 this.topologyTemplateService.createServiceProperty(this.component.uniqueId, newProperty)
1029 .subscribe((response) => {
1030 modal.instance.dynamicContent.instance.isLoading = false;
1031 const newProp: PropertyFEModel = this.propertiesUtils.convertAddPropertyBAToPropertyFE(response);
1032 this.instanceFePropertiesMap[this.component.uniqueId].push(newProp);
1033 modal.instance.close();
1035 modal.instance.dynamicContent.instance.isLoading = false;
1036 this.Notification.error({
1037 message: 'Failed to add property:' + error,
1041 }, () => !modal.instance.dynamicContent.instance.checkFormValidForSubmit()),
1042 new ButtonModel('Cancel', 'outline grey', () => {
1043 modal.instance.close();
1048 this.ModalService.addDynamicContentToModal(modal, PropertyCreatorComponent, {});
1049 modal.instance.open();
1052 /*** SEARCH RELATED FUNCTIONS ***/
1053 searchPropertiesInstances = (filterData:FilterPropertiesAssignmentData) => {
1054 let instanceBePropertiesMap:InstanceBePropertiesMap;
1055 this.componentServiceNg2
1056 .filterComponentInstanceProperties(this.component, filterData)
1057 .subscribe((response) => {
1058 this.processInstancePropertiesResponse(response, false);
1059 this.hierarchyPropertiesDisplayOptions.searchText = filterData.propertyName;//mark results in tree
1060 this.searchPropertyName = filterData.propertyName;//mark in table
1061 this.hierarchyNavTabs.triggerTabChange('Composition');
1062 this.propertiesNavigationData = [];
1063 this.displayClearSearch = true;
1064 }, (error) => {}); //ignore error
1068 clearSearch = () => {
1069 this.instancesNavigationData = this.instances;
1070 this.searchPropertyName = "";
1071 this.hierarchyPropertiesDisplayOptions.searchText = "";
1072 this.displayClearSearch = false;
1073 this.advanceSearch.clearAll();
1074 this.searchQuery = '';
1077 clickOnClearSearch = () => {
1079 this.selectFirstInstanceByDefault();
1080 this.hierarchyNavTabs.triggerTabChange('Composition');
1083 private isInput = (instanceType:string):boolean =>{
1084 return instanceType === ResourceType.VF || instanceType === ResourceType.PNF || instanceType === ResourceType.CVFC || instanceType === ResourceType.CR;