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) {
371 this.isValidChangedData = this.changedData.every((changedItem) => (<InputFEModel>changedItem).defaultValueObjIsValid && (<InputFEModel>changedItem).metadataIsValid);
372 } else if (this.isPoliciesTabSelected) {
373 this.isValidChangedData = this.changedData.every((changedItem) => (<InputFEModel>changedItem).defaultValueObjIsValid);
375 this.updateHasChangedData();
379 /*** HEIRARCHY/NAV RELATED FUNCTIONS ***/
382 * Handle select node in navigation area, and select the row in table
384 onPropertySelectedUpdate = ($event) => {
385 console.log("==>" + this.constructor.name + ": onPropertySelectedUpdate");
386 this.selectedFlatProperty = $event;
387 let parentProperty:PropertyFEModel = this.propertiesService.getParentPropertyFEModelFromPath(this.instanceFePropertiesMap[this.selectedFlatProperty.instanceName], this.selectedFlatProperty.path);
388 parentProperty.expandedChildPropertyId = this.selectedFlatProperty.path;
392 * When user select row in table, this will prepare the hirarchy object for the tree.
394 selectPropertyRow = (propertyRowSelectedEvent:PropertyRowSelectedEvent) => {
395 console.log("==>" + this.constructor.name + ": selectPropertyRow " + propertyRowSelectedEvent.propertyModel.name);
396 let property = propertyRowSelectedEvent.propertyModel;
397 let instanceName = propertyRowSelectedEvent.instanceName;
398 this.propertyStructureHeader = null;
400 // Build hirarchy tree for the navigation and update propertiesNavigationData with it.
401 if (!(this.selectedInstanceData instanceof ComponentInstance) || this.selectedInstanceData.originType !== ResourceType.VF) {
402 let simpleFlatProperty:Array<SimpleFlatProperty>;
403 if (property instanceof PropertyFEModel) {
404 simpleFlatProperty = this.hierarchyNavService.getSimplePropertiesTree(property, instanceName);
405 } else if (property instanceof DerivedFEProperty) {
406 // Need to find parent PropertyFEModel
407 let parentPropertyFEModel:PropertyFEModel = _.find(this.instanceFePropertiesMap[instanceName], (tmpFeProperty):boolean => {
408 return property.propertiesName.indexOf(tmpFeProperty.name)===0;
410 simpleFlatProperty = this.hierarchyNavService.getSimplePropertiesTree(parentPropertyFEModel, instanceName);
412 this.propertiesNavigationData = simpleFlatProperty;
415 // Update the header in the navigation tree with property name.
416 this.propertyStructureHeader = (property.propertiesName.split('#'))[0];
418 // Set selected property in table
419 this.selectedFlatProperty = this.hierarchyNavService.createSimpleFlatProperty(property, instanceName);
420 this.hierarchyNavTabs.triggerTabChange('Property Structure');
424 selectInstanceRow = ($event) => {//get instance name
425 this.selectedInstanceData = _.find(this.instancesNavigationData, (instance:ComponentInstance) => {
426 return instance.name == $event;
428 this.hierarchyNavTabs.triggerTabChange('Composition');
431 tabChanged = (event) => {
432 // stop if has changed properties
433 if (this.hasChangedData) {
434 this.propertyInputTabs.triggerTabChange(this.currentMainTab.title);
435 this.showUnsavedChangesAlert().then((proceed) => {
436 this.propertyInputTabs.selectTab(this.propertyInputTabs.tabs.find((tab) => tab.title === event.title));
442 console.log("==>" + this.constructor.name + ": tabChanged " + event);
443 this.currentMainTab = this.propertyInputTabs.tabs.find((tab) => tab.title === event.title);
444 this.isPropertiesTabSelected = this.currentMainTab.title === "Properties";
445 this.isInputsTabSelected = this.currentMainTab.title === "Inputs";
446 this.isPoliciesTabSelected = this.currentMainTab.title === "Policies";
447 this.propertyStructureHeader = null;
448 this.searchQuery = '';
453 /*** DECLARE PROPERTIES/INPUTS ***/
454 declareProperties = (): void => {
455 console.log("==>" + this.constructor.name + ": declareProperties");
457 let selectedComponentInstancesProperties: InstanceBePropertiesMap = new InstanceBePropertiesMap();
458 let selectedGroupInstancesProperties: InstanceBePropertiesMap = new InstanceBePropertiesMap();
459 let selectedPolicyInstancesProperties: InstanceBePropertiesMap = new InstanceBePropertiesMap();
460 let selectedComponentInstancesInputs: InstanceBePropertiesMap = new InstanceBePropertiesMap();
461 let instancesIds = this.keysPipe.transform(this.instanceFePropertiesMap, []);
463 angular.forEach(instancesIds, (instanceId: string): void => {
464 let selectedInstanceData: any = this.instances.find(instance => instance.uniqueId == instanceId);
465 if (selectedInstanceData instanceof ComponentInstance) {
466 if (!this.isInput(selectedInstanceData.originType)) {
467 // convert Property FE model -> Property BE model, extract only checked
468 selectedComponentInstancesProperties[instanceId] = this.propertiesService.getCheckedProperties(this.instanceFePropertiesMap[instanceId]);
470 selectedComponentInstancesInputs[instanceId] = this.propertiesService.getCheckedProperties(this.instanceFePropertiesMap[instanceId]);
472 } else if (selectedInstanceData instanceof GroupInstance) {
473 selectedGroupInstancesProperties[instanceId] = this.propertiesService.getCheckedProperties(this.instanceFePropertiesMap[instanceId]);
474 } else if (selectedInstanceData instanceof PolicyInstance) {
475 selectedPolicyInstancesProperties[instanceId] = this.propertiesService.getCheckedProperties(this.instanceFePropertiesMap[instanceId]);
479 let inputsToCreate: InstancePropertiesAPIMap = new InstancePropertiesAPIMap(selectedComponentInstancesInputs, selectedComponentInstancesProperties, selectedGroupInstancesProperties, selectedPolicyInstancesProperties);
481 //move changed capabilities properties from componentInstanceInputsMap obj to componentInstanceProperties
482 inputsToCreate.componentInstanceProperties[this.selectedInstanceData.uniqueId] =
483 (inputsToCreate.componentInstanceProperties[this.selectedInstanceData.uniqueId] || []).concat(
485 inputsToCreate.componentInstanceInputsMap[this.selectedInstanceData.uniqueId],
486 (prop: PropertyBEModel) => this.isCapabilityProperty(prop)
489 inputsToCreate.componentInstanceInputsMap[this.selectedInstanceData.uniqueId] = _.filter(
490 inputsToCreate.componentInstanceInputsMap[this.selectedInstanceData.uniqueId],
491 prop => !this.isCapabilityProperty(prop)
493 if (inputsToCreate.componentInstanceInputsMap[this.selectedInstanceData.uniqueId].length === 0) {
494 delete inputsToCreate.componentInstanceInputsMap[this.selectedInstanceData.uniqueId];
497 let isCapabilityPropertyChanged = false;
499 inputsToCreate.componentInstanceProperties[this.selectedInstanceData.uniqueId],
500 (prop: PropertyBEModel) => {
501 prop.name = prop.origName || prop.name;
502 if (this.isCapabilityProperty(prop)) {
503 isCapabilityPropertyChanged = true;
507 this.topologyTemplateService
508 .createInput(this.component, inputsToCreate, this.isSelf())
509 .subscribe((response) => {
510 this.setInputTabIndication(response.length);
511 this.checkedPropertiesCount = 0;
512 this.checkedChildPropertiesCount = 0;
513 _.forEach(response, (input: InputBEModel) => {
514 const newInput: InputFEModel = new InputFEModel(input);
515 this.inputsUtils.resetInputDefaultValue(newInput, input.defaultValue);
516 this.inputs.push(newInput);
517 this.updatePropertyValueAfterDeclare(newInput);
519 if (isCapabilityPropertyChanged) {
520 this.reloadInstanceCapabilities();
522 }, error => {}); //ignore error
525 declareListProperties = (): void => {
526 console.log('declareListProperties() - enter');
528 // get selected properties
529 let selectedComponentInstancesProperties: InstanceBePropertiesMap = new InstanceBePropertiesMap();
530 let selectedGroupInstancesProperties: InstanceBePropertiesMap = new InstanceBePropertiesMap();
531 let selectedPolicyInstancesProperties: InstanceBePropertiesMap = new InstanceBePropertiesMap();
532 let selectedComponentInstancesInputs: InstanceBePropertiesMap = new InstanceBePropertiesMap();
533 let instancesIds = new KeysPipe().transform(this.instanceFePropertiesMap, []);
534 let propertyNameList: Array<string> = [];
537 angular.forEach(instancesIds, (instanceId: string): void => {
538 console.log("instanceId="+instanceId);
540 let selectedInstanceData: any = this.instances.find(instance => instance.uniqueId == instanceId);
541 let checkedProperties: PropertyBEModel[] = this.propertiesService.getCheckedProperties(this.instanceFePropertiesMap[instanceId]);
543 if (selectedInstanceData instanceof ComponentInstance) {
544 if (!this.isInput(selectedInstanceData.originType)) {
545 // convert Property FE model -> Property BE model, extract only checked
546 selectedComponentInstancesProperties[instanceId] = checkedProperties;
548 selectedComponentInstancesInputs[instanceId] = checkedProperties;
550 } else if (selectedInstanceData instanceof GroupInstance) {
551 selectedGroupInstancesProperties[instanceId] = checkedProperties;
552 } else if (selectedInstanceData instanceof PolicyInstance) {
553 selectedPolicyInstancesProperties[instanceId] = checkedProperties;
556 angular.forEach(checkedProperties, (property: PropertyBEModel) => {
557 propertyNameList.push(property.name);
561 let inputsToCreate: InstancePropertiesAPIMap = new InstancePropertiesAPIMap(selectedComponentInstancesInputs, selectedComponentInstancesProperties, selectedGroupInstancesProperties, selectedPolicyInstancesProperties);
563 let modalTitle = 'Declare Properties as List Input';
564 const modal = this.ModalService.createCustomModal(new ModalModel(
566 modalTitle, /* title */
571 'blue', /* css class */
572 () => { /* callback */
573 let content:any = modal.instance.dynamicContent.instance;
576 let reglistInput: InstanceBePropertiesMap = new InstanceBePropertiesMap();
577 let typelist: any = PROPERTY_TYPES.LIST;
578 let uniID: any = insId;
579 let boolfalse: any = false;
580 let required: any = content.propertyModel.required;
584 "type": content.propertyModel.simpleType,
588 let schemaProp :any = {
589 "type": content.propertyModel.simpleType,
593 reglistInput.description = content.propertyModel.description;
594 reglistInput.name = content.propertyModel.name;
595 reglistInput.type = typelist;
596 reglistInput.schemaType = content.propertyModel.simpleType;
597 reglistInput.instanceUniqueId = uniID;
598 reglistInput.uniqueId = uniID;
599 reglistInput.required = required;
600 reglistInput.schema = schem;
601 reglistInput.schemaProperty = schemaProp;
604 componentInstInputsMap: content.inputsToCreate,
605 listInput: reglistInput
607 console.log("save button clicked. input=", input);
609 this.topologyTemplateService
610 .createListInput(this.component, input, this.isSelf())
611 .subscribe(response => {
612 this.setInputTabIndication(response.length);
613 this.checkedPropertiesCount = 0;
614 this.checkedChildPropertiesCount = 0;
615 _.forEach(response, (input: InputBEModel) => {
616 let newInput: InputFEModel = new InputFEModel(input);
617 this.inputsUtils.resetInputDefaultValue(newInput, input.defaultValue);
618 this.inputs.push(newInput);
619 // create list input does not return updated properties info, so need to reload
620 //this.updatePropertyValueAfterDeclare(newInput);
621 // Reload the whole instance for now - TODO: CHANGE THIS after the BE starts returning properties within the response, use commented code below instead!
622 this.changeSelectedInstance(this.selectedInstanceData);
624 modal.instance.close();
626 }, error => {}); //ignore error
629 /*, getDisabled: function */
631 new ButtonModel('Cancel', 'outline grey', () => {
632 modal.instance.close();
637 // 3rd arg is passed to DeclareListComponent instance
638 this.ModalService.addDynamicContentToModal(modal, DeclareListComponent, {properties: inputsToCreate, propertyNameList: propertyNameList});
639 modal.instance.open();
640 console.log('declareListProperties() - leave');
643 /*** DECLARE PROPERTIES/POLICIES ***/
644 declarePropertiesToPolicies = (): void => {
645 let selectedComponentInstancesProperties: InstanceBePropertiesMap = new InstanceBePropertiesMap();
646 let instancesIds = new KeysPipe().transform(this.instanceFePropertiesMap, []);
648 angular.forEach(instancesIds, (instanceId: string): void => {
649 let selectedInstanceData: any = this.instances.find(instance => instance.uniqueId == instanceId);
650 if (selectedInstanceData instanceof ComponentInstance) {
651 if (!this.isInput(selectedInstanceData.originType)) {
652 selectedComponentInstancesProperties[instanceId] = this.propertiesService.getCheckedProperties(this.instanceFePropertiesMap[instanceId]);
657 let policiesToCreate: InstancePropertiesAPIMap = new InstancePropertiesAPIMap(null, selectedComponentInstancesProperties, null, null);
658 this.loadingPolicies = true;
660 this.topologyTemplateService
661 .createPolicy(this.component, policiesToCreate, this.isSelf())
662 .subscribe(response => {
663 this.setPolicyTabIndication(response.length);
664 this.checkedPropertiesCount = 0;
665 this.displayPoliciesAsDeclared(response);
666 this.loadingPolicies = false;
671 displayPoliciesAsDeclared = (policies) => {
672 _.forEach(policies, (policy: any) => {
673 let newPolicy: InputFEModel = new InputFEModel(policy);
674 this.inputsUtils.resetInputDefaultValue(newPolicy, policy.defaultValue);
675 newPolicy.relatedPropertyName = policy.name;
676 newPolicy.relatedPropertyValue = policy.value;
677 this.updatePropertyValueAfterDeclare(newPolicy);
678 this.policies.push(policy);
682 saveChangedData = ():Promise<(PropertyBEModel|InputBEModel)[]> => {
683 return new Promise((resolve, reject) => {
684 if (!this.isValidChangedData) {
685 reject('Changed data is invalid - cannot save!');
688 if (!this.changedData.length) {
693 // make request and its handlers
695 let handleSuccess, handleError;
696 let changedInputsProperties = [], changedCapabilitiesProperties = [];
697 if (this.isPropertiesTabSelected) {
698 const changedProperties: PropertyBEModel[] = this.changedData.map((changedProp) => {
699 changedProp = <PropertyFEModel>changedProp;
700 const propBE = new PropertyBEModel(changedProp);
701 propBE.toscaPresentation = new ToscaPresentationData();
702 propBE.toscaPresentation.ownerId = changedProp.parentUniqueId;
703 propBE.value = changedProp.getJSONValue();
704 propBE.name = changedProp.origName || changedProp.name;
705 delete propBE.origName;
708 changedCapabilitiesProperties = _.filter(changedProperties, prop => this.isCapabilityProperty(prop));
710 if (this.selectedInstanceData instanceof ComponentInstance) {
711 if (this.isInput(this.selectedInstanceData.originType)) {
712 changedInputsProperties = _.filter(changedProperties, prop => !this.isCapabilityProperty(prop));
713 if (changedInputsProperties.length && changedCapabilitiesProperties.length) {
714 request = Observable.forkJoin(
715 this.componentInstanceServiceNg2.updateInstanceInputs(this.component, this.selectedInstanceData.uniqueId, changedInputsProperties),
716 this.componentInstanceServiceNg2.updateInstanceProperties(this.component.componentType, this.component.uniqueId,
717 this.selectedInstanceData.uniqueId, changedCapabilitiesProperties)
720 else if (changedInputsProperties.length) {
721 request = this.componentInstanceServiceNg2
722 .updateInstanceInputs(this.component, this.selectedInstanceData.uniqueId, changedInputsProperties);
724 else if (changedCapabilitiesProperties.length) {
725 request = this.componentInstanceServiceNg2
726 .updateInstanceProperties(this.component.componentType, this.component.uniqueId, this.selectedInstanceData.uniqueId, changedCapabilitiesProperties);
728 handleSuccess = (response) => {
729 // reset each changed property with new value and remove it from changed properties list
730 response.forEach((resInput) => {
731 const changedProp = <PropertyFEModel>this.changedData.shift();
732 this.propertiesUtils.resetPropertyValue(changedProp, resInput.value);
734 console.log('updated instance inputs:', response);
738 request = this.topologyTemplateService.updateServiceProperties(this.component.uniqueId, _.map(changedProperties, cp => {
739 delete cp.constraints;
743 request = this.componentInstanceServiceNg2
744 .updateInstanceProperties(this.component.componentType, this.component.uniqueId, this.selectedInstanceData.uniqueId, changedProperties);
746 handleSuccess = (response) => {
747 // reset each changed property with new value and remove it from changed properties list
748 response.forEach((resProp) => {
749 const changedProp = <PropertyFEModel>this.changedData.shift();
750 this.propertiesUtils.resetPropertyValue(changedProp, resProp.value);
753 console.log("updated instance properties: ", response);
756 } else if (this.selectedInstanceData instanceof GroupInstance) {
757 request = this.componentInstanceServiceNg2
758 .updateComponentGroupInstanceProperties(this.component.componentType, this.component.uniqueId, this.selectedInstanceData.uniqueId, changedProperties);
759 handleSuccess = (response) => {
760 // reset each changed property with new value and remove it from changed properties list
761 response.forEach((resProp) => {
762 const changedProp = <PropertyFEModel>this.changedData.shift();
763 this.propertiesUtils.resetPropertyValue(changedProp, resProp.value);
766 console.log("updated group instance properties: ", response);
768 } else if (this.selectedInstanceData instanceof PolicyInstance) {
769 request = this.componentInstanceServiceNg2
770 .updateComponentPolicyInstanceProperties(this.component.componentType, this.component.uniqueId, this.selectedInstanceData.uniqueId, changedProperties);
771 handleSuccess = (response) => {
772 // reset each changed property with new value and remove it from changed properties list
773 response.forEach((resProp) => {
774 const changedProp = <PropertyFEModel>this.changedData.shift();
775 this.propertiesUtils.resetPropertyValue(changedProp, resProp.value);
778 console.log("updated policy instance properties: ", response);
781 } else if (this.isInputsTabSelected) {
783 const changedInputs: InputBEModel[] = this.changedData.map((changedInput) => {
784 changedInput = <InputFEModel>changedInput;
785 const inputBE = new InputBEModel(changedInput);
786 inputBE.defaultValue = changedInput.getJSONDefaultValue();
789 request = this.componentServiceNg2
790 .updateComponentInputs(this.component, changedInputs);
791 handleSuccess = (response) => {
792 // reset each changed property with new value and remove it from changed properties list
793 response.forEach((resInput) => {
794 const changedInput = <InputFEModel>this.changedData.shift();
795 this.inputsUtils.resetInputDefaultValue(changedInput, resInput.defaultValue);
796 changedInput.required = resInput.required;
797 changedInput.requiredOrig = resInput.required;
799 console.log("updated the component inputs and got this response: ", response);
803 this.savingChangedData = true;
806 this.savingChangedData = false;
807 if (changedCapabilitiesProperties.length) {
808 this.reloadInstanceCapabilities();
810 handleSuccess && handleSuccess(response);
811 this.updateHasChangedData();
815 this.savingChangedData = false;
816 handleError && handleError(error);
817 this.updateHasChangedData();
824 reloadInstanceCapabilities = (): void => {
825 let currentInstanceIndex = _.findIndex(this.instances, instance => instance.uniqueId == this.selectedInstanceData.uniqueId);
826 this.componentServiceNg2.getComponentResourceInstances(this.component).subscribe(result => {
827 let instanceCapabilitiesData: CapabilitiesGroup = _.reduce(result.componentInstances, (res, instance) => {
828 if (instance.uniqueId === this.selectedInstanceData.uniqueId) {
829 return instance.capabilities;
832 }, new CapabilitiesGroup());
833 (<ComponentInstance>this.instances[currentInstanceIndex]).capabilities = instanceCapabilitiesData;
837 reverseChangedData = ():void => {
838 // make reverse item handler
839 let handleReverseItem;
840 if (this.isPropertiesTabSelected) {
841 handleReverseItem = (changedItem) => {
842 changedItem = <PropertyFEModel>changedItem;
843 this.propertiesUtils.resetPropertyValue(changedItem, changedItem.value);
844 this.checkedPropertiesCount = 0;
845 this.checkedChildPropertiesCount = 0;
847 } else if (this.isInputsTabSelected) {
848 handleReverseItem = (changedItem) => {
849 changedItem = <InputFEModel>changedItem;
850 this.inputsUtils.resetInputDefaultValue(changedItem, changedItem.defaultValue);
851 changedItem.resetMetadata();
852 changedItem.required = changedItem.requiredOrig;
856 this.changedData.forEach(handleReverseItem);
857 this.changedData = [];
858 this.updateHasChangedData();
861 updateHasChangedData = ():boolean => {
862 const curHasChangedData:boolean = (this.changedData.length > 0);
863 if (curHasChangedData !== this.hasChangedData) {
864 this.hasChangedData = curHasChangedData;
865 if(this.hasChangedData) {
866 this.EventListenerService.notifyObservers(EVENTS.ON_WORKSPACE_UNSAVED_CHANGES, this.hasChangedData, this.showUnsavedChangesAlert);
868 this.EventListenerService.notifyObservers(EVENTS.ON_WORKSPACE_UNSAVED_CHANGES, false);
871 return this.hasChangedData;
874 doSaveChangedData = (onSuccessFunction?:Function, onError?:Function):void => {
875 this.saveChangedData().then(
877 this.Notification.success({
878 message: 'Successfully saved changes',
881 if(onSuccessFunction) onSuccessFunction();
882 if(this.isPropertiesTabSelected){
883 this.checkedPropertiesCount = 0;
884 this.checkedChildPropertiesCount = 0;
888 this.Notification.error({
889 message: 'Failed to save changes!',
892 if(onError) onError();
897 showUnsavedChangesAlert = ():Promise<any> => {
898 let modalTitle:string;
899 if (this.isPropertiesTabSelected) {
900 modalTitle = `Unsaved properties for ${this.selectedInstanceData.name}`;
901 } else if (this.isInputsTabSelected) {
902 modalTitle = `Unsaved inputs for ${this.component.name}`;
905 return new Promise<any>((resolve, reject) => {
906 const modal = this.ModalServiceSdcUI.openCustomModal(
910 type: SdcUiCommon.ModalType.custom,
911 testId: "navigate-modal",
914 {id: 'cancelButton', text: 'Cancel', type: SdcUiCommon.ButtonType.secondary, size: 'xsm', closeModal: true, callback: () => reject()},
915 {id: 'discardButton', text: 'Discard', type: SdcUiCommon.ButtonType.secondary, size: 'xsm', closeModal: true, callback: () => { this.reverseChangedData(); resolve()}},
916 {id: 'saveButton', text: 'Save', type: SdcUiCommon.ButtonType.primary, size: 'xsm', closeModal: true, disabled: !this.isValidChangedData, callback: () => this.doSaveChangedData(resolve, reject)}
917 ] as SdcUiCommon.IModalButtonComponent[]
918 } as SdcUiCommon.IModalConfig, UnsavedChangesComponent, {isValidChangedData: this.isValidChangedData});
923 updatePropertyValueAfterDeclare = (input: InputFEModel) => {
924 if (this.instanceFePropertiesMap[input.instanceUniqueId]) {
925 const instanceName = input.instanceUniqueId.slice(input.instanceUniqueId.lastIndexOf('.') + 1);
926 const propertyForUpdatindVal = _.find(this.instanceFePropertiesMap[input.instanceUniqueId], (feProperty: PropertyFEModel) => {
927 return feProperty.name == input.relatedPropertyName &&
928 (feProperty.name == input.relatedPropertyName || input.name === instanceName.concat('_').concat(feProperty.name.replace(/[.]/g, '_')));
930 const inputPath = (input.inputPath && input.inputPath != propertyForUpdatindVal.name) ? input.inputPath : undefined;
931 propertyForUpdatindVal.setAsDeclared(inputPath); //set prop as declared before assigning value
932 this.propertiesService.disableRelatedProperties(propertyForUpdatindVal, inputPath);
933 this.propertiesUtils.resetPropertyValue(propertyForUpdatindVal, input.relatedPropertyValue, inputPath);
937 //used for declare button, to keep count of newly checked properties (and ignore declared properties)
938 updateCheckedPropertyCount = (increment: boolean): void => {
939 this.checkedPropertiesCount += (increment) ? 1 : -1;
940 console.log("CheckedProperties count is now.... " + this.checkedPropertiesCount);
943 updateCheckedChildPropertyCount = (increment: boolean): void => {
944 this.checkedChildPropertiesCount += (increment) ? 1 : -1;
947 setInputTabIndication = (numInputs: number): void => {
948 this.propertyInputTabs.setTabIndication('Inputs', numInputs);
951 setPolicyTabIndication = (numPolicies: number): void => {
952 this.propertyInputTabs.setTabIndication('Policies', numPolicies);
955 resetUnsavedChangesForInput = (input:InputFEModel) => {
956 this.inputsUtils.resetInputDefaultValue(input, input.defaultValue);
957 this.changedData = this.changedData.filter((changedItem) => changedItem.uniqueId !== input.uniqueId);
958 this.updateHasChangedData();
961 deleteInput = (input: InputFEModel) => {
962 //reset any unsaved changes to the input before deleting it
963 this.resetUnsavedChangesForInput(input);
965 console.log("==>" + this.constructor.name + ": deleteInput");
966 let inputToDelete = new InputBEModel(input);
968 this.componentServiceNg2
969 .deleteInput(this.component, inputToDelete)
970 .subscribe(response => {
971 this.inputs = this.inputs.filter(input => input.uniqueId !== response.uniqueId);
973 //Reload the whole instance for now - TODO: CHANGE THIS after the BE starts returning properties within the response, use commented code below instead!
974 this.changeSelectedInstance(this.selectedInstanceData);
975 // let instanceFeProperties = this.instanceFePropertiesMap[this.getInstanceUniqueId(input.instanceName)];
977 // if (instanceFeProperties) {
978 // let propToEnable: PropertyFEModel = instanceFeProperties.find((prop) => {
979 // return prop.name == input.propertyName;
982 // if (propToEnable) {
983 // if (propToEnable.name == response.inputPath) response.inputPath = null;
984 // propToEnable.setNonDeclared(response.inputPath);
985 // //this.propertiesUtils.resetPropertyValue(propToEnable, newValue, response.inputPath);
986 // this.propertiesService.undoDisableRelatedProperties(propToEnable, response.inputPath);
989 }, error => {}); //ignore error
992 deletePolicy = (policy: PolicyInstance) => {
993 this.loadingPolicies = true;
994 this.topologyTemplateService
995 .deletePolicy(this.component, policy)
996 .subscribe((response) => {
997 this.policies = this.policies.filter(policy => policy.uniqueId !== response.uniqueId);
998 //Reload the whole instance for now - TODO: CHANGE THIS after the BE starts returning properties within the response, use commented code below instead!
999 this.changeSelectedInstance(this.selectedInstanceData);
1000 this.loadingPolicies = false;
1004 deleteProperty = (property: PropertyFEModel) => {
1005 const propertyToDelete = new PropertyFEModel(property);
1006 this.loadingProperties = true;
1007 const feMap = this.instanceFePropertiesMap;
1008 this.topologyTemplateService
1009 .deleteServiceProperty(this.component.uniqueId, propertyToDelete)
1010 .subscribe((response) => {
1011 const props = feMap[this.component.uniqueId];
1012 props.splice(props.findIndex(p => p.uniqueId === response),1);
1013 this.loadingProperties = false;
1015 this.loadingProperties = false;
1016 console.error(error);
1020 /*** addProperty ***/
1021 addProperty = () => {
1022 let modalTitle = 'Add Property';
1023 let modal = this.ModalService.createCustomModal(new ModalModel(
1028 new ButtonModel('Save', 'blue', () => {
1029 modal.instance.dynamicContent.instance.isLoading = true;
1030 const newProperty: PropertyBEModel = modal.instance.dynamicContent.instance.propertyModel;
1031 this.topologyTemplateService.createServiceProperty(this.component.uniqueId, newProperty)
1032 .subscribe((response) => {
1033 modal.instance.dynamicContent.instance.isLoading = false;
1034 const newProp: PropertyFEModel = this.propertiesUtils.convertAddPropertyBAToPropertyFE(response);
1035 this.instanceFePropertiesMap[this.component.uniqueId].push(newProp);
1036 modal.instance.close();
1038 modal.instance.dynamicContent.instance.isLoading = false;
1039 this.Notification.error({
1040 message: 'Failed to add property:' + error,
1044 }, () => !modal.instance.dynamicContent.instance.checkFormValidForSubmit()),
1045 new ButtonModel('Cancel', 'outline grey', () => {
1046 modal.instance.close();
1051 this.ModalService.addDynamicContentToModal(modal, PropertyCreatorComponent, {});
1052 modal.instance.open();
1055 /*** SEARCH RELATED FUNCTIONS ***/
1056 searchPropertiesInstances = (filterData:FilterPropertiesAssignmentData) => {
1057 let instanceBePropertiesMap:InstanceBePropertiesMap;
1058 this.componentServiceNg2
1059 .filterComponentInstanceProperties(this.component, filterData)
1060 .subscribe((response) => {
1061 this.processInstancePropertiesResponse(response, false);
1062 this.hierarchyPropertiesDisplayOptions.searchText = filterData.propertyName;//mark results in tree
1063 this.searchPropertyName = filterData.propertyName;//mark in table
1064 this.hierarchyNavTabs.triggerTabChange('Composition');
1065 this.propertiesNavigationData = [];
1066 this.displayClearSearch = true;
1067 }, (error) => {}); //ignore error
1071 clearSearch = () => {
1072 this.instancesNavigationData = this.instances;
1073 this.searchPropertyName = "";
1074 this.hierarchyPropertiesDisplayOptions.searchText = "";
1075 this.displayClearSearch = false;
1076 this.advanceSearch.clearAll();
1077 this.searchQuery = '';
1080 clickOnClearSearch = () => {
1082 this.selectFirstInstanceByDefault();
1083 this.hierarchyNavTabs.triggerTabChange('Composition');
1086 private isInput = (instanceType:string):boolean =>{
1087 return instanceType === ResourceType.VF || instanceType === ResourceType.PNF || instanceType === ResourceType.CVFC || instanceType === ResourceType.CR;