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.hasChanged();
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;
577 let required: any = content.propertyModel.required;
581 "type": content.propertyModel.simpleType,
585 let schemaProp :any = {
586 "type": content.propertyModel.simpleType,
590 reglistInput.description = content.propertyModel.description;
591 reglistInput.name = content.propertyModel.name;
592 reglistInput.type = typelist;
593 reglistInput.schemaType = content.propertyModel.simpleType;
594 reglistInput.instanceUniqueId = uniID;
595 reglistInput.uniqueId = uniID;
596 reglistInput.required = required;
597 reglistInput.schema = schem;
598 reglistInput.schemaProperty = schemaProp;
601 componentInstInputsMap: content.inputsToCreate,
602 listInput: reglistInput
604 console.log("save button clicked. input=", input);
606 this.topologyTemplateService
607 .createListInput(this.component.uniqueId, input, this.isSelf())
608 .subscribe(response => {
609 this.setInputTabIndication(response.length);
610 this.checkedPropertiesCount = 0;
611 this.checkedChildPropertiesCount = 0;
612 _.forEach(response, (input: InputBEModel) => {
613 let newInput: InputFEModel = new InputFEModel(input);
614 this.inputsUtils.resetInputDefaultValue(newInput, input.defaultValue);
615 this.inputs.push(newInput);
616 // create list input does not return updated properties info, so need to reload
617 //this.updatePropertyValueAfterDeclare(newInput);
618 // Reload the whole instance for now - TODO: CHANGE THIS after the BE starts returning properties within the response, use commented code below instead!
619 this.changeSelectedInstance(this.selectedInstanceData);
621 modal.instance.close();
623 }, error => {}); //ignore error
626 /*, getDisabled: function */
628 new ButtonModel('Cancel', 'outline grey', () => {
629 modal.instance.close();
634 // 3rd arg is passed to DeclareListComponent instance
635 this.ModalService.addDynamicContentToModal(modal, DeclareListComponent, {properties: inputsToCreate, propertyNameList: propertyNameList});
636 modal.instance.open();
637 console.log('declareListProperties() - leave');
640 /*** DECLARE PROPERTIES/POLICIES ***/
641 declarePropertiesToPolicies = (): void => {
642 let selectedComponentInstancesProperties: InstanceBePropertiesMap = new InstanceBePropertiesMap();
643 let instancesIds = new KeysPipe().transform(this.instanceFePropertiesMap, []);
645 angular.forEach(instancesIds, (instanceId: string): void => {
646 let selectedInstanceData: any = this.instances.find(instance => instance.uniqueId == instanceId);
647 if (selectedInstanceData instanceof ComponentInstance) {
648 if (!this.isInput(selectedInstanceData.originType)) {
649 selectedComponentInstancesProperties[instanceId] = this.propertiesService.getCheckedProperties(this.instanceFePropertiesMap[instanceId]);
654 let policiesToCreate: InstancePropertiesAPIMap = new InstancePropertiesAPIMap(null, selectedComponentInstancesProperties, null, null);
655 this.loadingPolicies = true;
657 this.topologyTemplateService
658 .createPolicy(this.component, policiesToCreate, this.isSelf())
659 .subscribe(response => {
660 this.setPolicyTabIndication(response.length);
661 this.checkedPropertiesCount = 0;
662 this.displayPoliciesAsDeclared(response);
663 this.loadingPolicies = false;
668 displayPoliciesAsDeclared = (policies) => {
669 _.forEach(policies, (policy: any) => {
670 let newPolicy: InputFEModel = new InputFEModel(policy);
671 this.inputsUtils.resetInputDefaultValue(newPolicy, policy.defaultValue);
672 newPolicy.relatedPropertyName = policy.name;
673 newPolicy.relatedPropertyValue = policy.value;
674 this.updatePropertyValueAfterDeclare(newPolicy);
675 this.policies.push(policy);
679 saveChangedData = ():Promise<(PropertyBEModel|InputBEModel)[]> => {
680 return new Promise((resolve, reject) => {
681 if (!this.isValidChangedData) {
682 reject('Changed data is invalid - cannot save!');
685 if (!this.changedData.length) {
690 // make request and its handlers
692 let handleSuccess, handleError;
693 let changedInputsProperties = [], changedCapabilitiesProperties = [];
694 if (this.isPropertiesTabSelected) {
695 const changedProperties: PropertyBEModel[] = this.changedData.map((changedProp) => {
696 changedProp = <PropertyFEModel>changedProp;
697 const propBE = new PropertyBEModel(changedProp);
698 propBE.toscaPresentation = new ToscaPresentationData();
699 propBE.toscaPresentation.ownerId = changedProp.parentUniqueId;
700 propBE.value = changedProp.getJSONValue();
701 propBE.name = changedProp.origName || changedProp.name;
702 delete propBE.origName;
705 changedCapabilitiesProperties = _.filter(changedProperties, prop => this.isCapabilityProperty(prop));
707 if (this.selectedInstanceData instanceof ComponentInstance) {
708 if (this.isInput(this.selectedInstanceData.originType)) {
709 changedInputsProperties = _.filter(changedProperties, prop => !this.isCapabilityProperty(prop));
710 if (changedInputsProperties.length && changedCapabilitiesProperties.length) {
711 request = Observable.forkJoin(
712 this.componentInstanceServiceNg2.updateInstanceInputs(this.component, this.selectedInstanceData.uniqueId, changedInputsProperties),
713 this.componentInstanceServiceNg2.updateInstanceProperties(this.component.componentType, this.component.uniqueId,
714 this.selectedInstanceData.uniqueId, changedCapabilitiesProperties)
717 else if (changedInputsProperties.length) {
718 request = this.componentInstanceServiceNg2
719 .updateInstanceInputs(this.component, this.selectedInstanceData.uniqueId, changedInputsProperties);
721 else if (changedCapabilitiesProperties.length) {
722 request = this.componentInstanceServiceNg2
723 .updateInstanceProperties(this.component.componentType, this.component.uniqueId, this.selectedInstanceData.uniqueId, changedCapabilitiesProperties);
725 handleSuccess = (response) => {
726 // reset each changed property with new value and remove it from changed properties list
727 response.forEach((resInput) => {
728 const changedProp = <PropertyFEModel>this.changedData.shift();
729 this.propertiesUtils.resetPropertyValue(changedProp, resInput.value);
731 console.log('updated instance inputs:', response);
735 request = this.topologyTemplateService.updateServiceProperties(this.component.uniqueId, _.map(changedProperties, cp => {
736 delete cp.constraints;
740 request = this.componentInstanceServiceNg2
741 .updateInstanceProperties(this.component.componentType, this.component.uniqueId, this.selectedInstanceData.uniqueId, changedProperties);
743 handleSuccess = (response) => {
744 // reset each changed property with new value and remove it from changed properties list
745 response.forEach((resProp) => {
746 const changedProp = <PropertyFEModel>this.changedData.shift();
747 this.propertiesUtils.resetPropertyValue(changedProp, resProp.value);
750 console.log("updated instance properties: ", response);
753 } else if (this.selectedInstanceData instanceof GroupInstance) {
754 request = this.componentInstanceServiceNg2
755 .updateComponentGroupInstanceProperties(this.component.componentType, this.component.uniqueId, this.selectedInstanceData.uniqueId, changedProperties);
756 handleSuccess = (response) => {
757 // reset each changed property with new value and remove it from changed properties list
758 response.forEach((resProp) => {
759 const changedProp = <PropertyFEModel>this.changedData.shift();
760 this.propertiesUtils.resetPropertyValue(changedProp, resProp.value);
763 console.log("updated group instance properties: ", response);
765 } else if (this.selectedInstanceData instanceof PolicyInstance) {
766 request = this.componentInstanceServiceNg2
767 .updateComponentPolicyInstanceProperties(this.component.componentType, this.component.uniqueId, this.selectedInstanceData.uniqueId, changedProperties);
768 handleSuccess = (response) => {
769 // reset each changed property with new value and remove it from changed properties list
770 response.forEach((resProp) => {
771 const changedProp = <PropertyFEModel>this.changedData.shift();
772 this.propertiesUtils.resetPropertyValue(changedProp, resProp.value);
775 console.log("updated policy instance properties: ", response);
778 } else if (this.isInputsTabSelected) {
780 const changedInputs: InputBEModel[] = this.changedData.map((changedInput) => {
781 changedInput = <InputFEModel>changedInput;
782 const inputBE = new InputBEModel(changedInput);
783 inputBE.defaultValue = changedInput.getJSONDefaultValue();
786 request = this.componentServiceNg2
787 .updateComponentInputs(this.component, changedInputs);
788 handleSuccess = (response) => {
789 // reset each changed property with new value and remove it from changed properties list
790 response.forEach((resInput) => {
791 const changedInput = <InputFEModel>this.changedData.shift();
792 this.inputsUtils.resetInputDefaultValue(changedInput, resInput.defaultValue);
793 changedInput.required = resInput.required;
794 changedInput.requiredOrig = resInput.required;
796 console.log("updated the component inputs and got this response: ", response);
800 this.savingChangedData = true;
803 this.savingChangedData = false;
804 if (changedCapabilitiesProperties.length) {
805 this.reloadInstanceCapabilities();
807 handleSuccess && handleSuccess(response);
808 this.updateHasChangedData();
812 this.savingChangedData = false;
813 handleError && handleError(error);
814 this.updateHasChangedData();
821 reloadInstanceCapabilities = (): void => {
822 let currentInstanceIndex = _.findIndex(this.instances, instance => instance.uniqueId == this.selectedInstanceData.uniqueId);
823 this.componentServiceNg2.getComponentResourceInstances(this.component).subscribe(result => {
824 let instanceCapabilitiesData: CapabilitiesGroup = _.reduce(result.componentInstances, (res, instance) => {
825 if (instance.uniqueId === this.selectedInstanceData.uniqueId) {
826 return instance.capabilities;
829 }, new CapabilitiesGroup());
830 (<ComponentInstance>this.instances[currentInstanceIndex]).capabilities = instanceCapabilitiesData;
834 reverseChangedData = ():void => {
835 // make reverse item handler
836 let handleReverseItem;
837 if (this.isPropertiesTabSelected) {
838 handleReverseItem = (changedItem) => {
839 changedItem = <PropertyFEModel>changedItem;
840 this.propertiesUtils.resetPropertyValue(changedItem, changedItem.value);
841 this.checkedPropertiesCount = 0;
842 this.checkedChildPropertiesCount = 0;
844 } else if (this.isInputsTabSelected) {
845 handleReverseItem = (changedItem) => {
846 changedItem = <InputFEModel>changedItem;
847 this.inputsUtils.resetInputDefaultValue(changedItem, changedItem.defaultValue);
848 changedItem.required = changedItem.requiredOrig;
852 this.changedData.forEach(handleReverseItem);
853 this.changedData = [];
854 this.updateHasChangedData();
857 updateHasChangedData = ():boolean => {
858 const curHasChangedData:boolean = (this.changedData.length > 0);
859 if (curHasChangedData !== this.hasChangedData) {
860 this.hasChangedData = curHasChangedData;
861 if(this.hasChangedData) {
862 this.EventListenerService.notifyObservers(EVENTS.ON_WORKSPACE_UNSAVED_CHANGES, this.hasChangedData, this.showUnsavedChangesAlert);
864 this.EventListenerService.notifyObservers(EVENTS.ON_WORKSPACE_UNSAVED_CHANGES, false);
867 return this.hasChangedData;
870 doSaveChangedData = (onSuccessFunction?:Function, onError?:Function):void => {
871 this.saveChangedData().then(
873 this.Notification.success({
874 message: 'Successfully saved changes',
877 if(onSuccessFunction) onSuccessFunction();
878 if(this.isPropertiesTabSelected){
879 this.checkedPropertiesCount = 0;
880 this.checkedChildPropertiesCount = 0;
884 this.Notification.error({
885 message: 'Failed to save changes!',
888 if(onError) onError();
893 showUnsavedChangesAlert = ():Promise<any> => {
894 let modalTitle:string;
895 if (this.isPropertiesTabSelected) {
896 modalTitle = `Unsaved properties for ${this.selectedInstanceData.name}`;
897 } else if (this.isInputsTabSelected) {
898 modalTitle = `Unsaved inputs for ${this.component.name}`;
901 return new Promise<any>((resolve, reject) => {
902 const modal = this.ModalServiceSdcUI.openCustomModal(
906 type: SdcUiCommon.ModalType.custom,
907 testId: "navigate-modal",
910 {id: 'cancelButton', text: 'Cancel', type: SdcUiCommon.ButtonType.secondary, size: 'xsm', closeModal: true, callback: () => reject()},
911 {id: 'discardButton', text: 'Discard', type: SdcUiCommon.ButtonType.secondary, size: 'xsm', closeModal: true, callback: () => { this.reverseChangedData(); resolve()}},
912 {id: 'saveButton', text: 'Save', type: SdcUiCommon.ButtonType.primary, size: 'xsm', closeModal: true, disabled: !this.isValidChangedData, callback: () => this.doSaveChangedData(resolve, reject)}
913 ] as SdcUiCommon.IModalButtonComponent[]
914 } as SdcUiCommon.IModalConfig, UnsavedChangesComponent, {isValidChangedData: this.isValidChangedData});
919 updatePropertyValueAfterDeclare = (input: InputFEModel) => {
920 if (this.instanceFePropertiesMap[input.instanceUniqueId]) {
921 const instanceName = input.instanceUniqueId.slice(input.instanceUniqueId.lastIndexOf('.') + 1);
922 const propertyForUpdatindVal = _.find(this.instanceFePropertiesMap[input.instanceUniqueId], (feProperty: PropertyFEModel) => {
923 return feProperty.name == input.relatedPropertyName &&
924 (feProperty.name == input.relatedPropertyName || input.name === instanceName.concat('_').concat(feProperty.name.replace(/[.]/g, '_')));
926 const inputPath = (input.inputPath && input.inputPath != propertyForUpdatindVal.name) ? input.inputPath : undefined;
927 propertyForUpdatindVal.setAsDeclared(inputPath); //set prop as declared before assigning value
928 this.propertiesService.disableRelatedProperties(propertyForUpdatindVal, inputPath);
929 this.propertiesUtils.resetPropertyValue(propertyForUpdatindVal, input.relatedPropertyValue, inputPath);
933 //used for declare button, to keep count of newly checked properties (and ignore declared properties)
934 updateCheckedPropertyCount = (increment: boolean): void => {
935 this.checkedPropertiesCount += (increment) ? 1 : -1;
936 console.log("CheckedProperties count is now.... " + this.checkedPropertiesCount);
939 updateCheckedChildPropertyCount = (increment: boolean): void => {
940 this.checkedChildPropertiesCount += (increment) ? 1 : -1;
943 setInputTabIndication = (numInputs: number): void => {
944 this.propertyInputTabs.setTabIndication('Inputs', numInputs);
947 setPolicyTabIndication = (numPolicies: number): void => {
948 this.propertyInputTabs.setTabIndication('Policies', numPolicies);
951 resetUnsavedChangesForInput = (input:InputFEModel) => {
952 this.inputsUtils.resetInputDefaultValue(input, input.defaultValue);
953 this.changedData = this.changedData.filter((changedItem) => changedItem.uniqueId !== input.uniqueId);
954 this.updateHasChangedData();
957 deleteInput = (input: InputFEModel) => {
958 //reset any unsaved changes to the input before deleting it
959 this.resetUnsavedChangesForInput(input);
961 console.log("==>" + this.constructor.name + ": deleteInput");
962 let inputToDelete = new InputBEModel(input);
964 this.componentServiceNg2
965 .deleteInput(this.component, inputToDelete)
966 .subscribe(response => {
967 this.inputs = this.inputs.filter(input => input.uniqueId !== response.uniqueId);
969 //Reload the whole instance for now - TODO: CHANGE THIS after the BE starts returning properties within the response, use commented code below instead!
970 this.changeSelectedInstance(this.selectedInstanceData);
971 // let instanceFeProperties = this.instanceFePropertiesMap[this.getInstanceUniqueId(input.instanceName)];
973 // if (instanceFeProperties) {
974 // let propToEnable: PropertyFEModel = instanceFeProperties.find((prop) => {
975 // return prop.name == input.propertyName;
978 // if (propToEnable) {
979 // if (propToEnable.name == response.inputPath) response.inputPath = null;
980 // propToEnable.setNonDeclared(response.inputPath);
981 // //this.propertiesUtils.resetPropertyValue(propToEnable, newValue, response.inputPath);
982 // this.propertiesService.undoDisableRelatedProperties(propToEnable, response.inputPath);
985 }, error => {}); //ignore error
988 deletePolicy = (policy: PolicyInstance) => {
989 this.loadingPolicies = true;
990 this.topologyTemplateService
991 .deletePolicy(this.component, policy)
992 .subscribe((response) => {
993 this.policies = this.policies.filter(policy => policy.uniqueId !== response.uniqueId);
994 //Reload the whole instance for now - TODO: CHANGE THIS after the BE starts returning properties within the response, use commented code below instead!
995 this.changeSelectedInstance(this.selectedInstanceData);
996 this.loadingPolicies = false;
1000 deleteProperty = (property: PropertyFEModel) => {
1001 const propertyToDelete = new PropertyFEModel(property);
1002 this.loadingProperties = true;
1003 const feMap = this.instanceFePropertiesMap;
1004 this.topologyTemplateService
1005 .deleteServiceProperty(this.component.uniqueId, propertyToDelete)
1006 .subscribe((response) => {
1007 const props = feMap[this.component.uniqueId];
1008 props.splice(props.findIndex(p => p.uniqueId === response),1);
1009 this.loadingProperties = false;
1011 this.loadingProperties = false;
1012 console.error(error);
1016 /*** addProperty ***/
1017 addProperty = () => {
1018 let modalTitle = 'Add Property';
1019 let modal = this.ModalService.createCustomModal(new ModalModel(
1024 new ButtonModel('Save', 'blue', () => {
1025 modal.instance.dynamicContent.instance.isLoading = true;
1026 const newProperty: PropertyBEModel = modal.instance.dynamicContent.instance.propertyModel;
1027 this.topologyTemplateService.createServiceProperty(this.component.uniqueId, newProperty)
1028 .subscribe((response) => {
1029 modal.instance.dynamicContent.instance.isLoading = false;
1030 const newProp: PropertyFEModel = this.propertiesUtils.convertAddPropertyBAToPropertyFE(response);
1031 this.instanceFePropertiesMap[this.component.uniqueId].push(newProp);
1032 modal.instance.close();
1034 modal.instance.dynamicContent.instance.isLoading = false;
1035 this.Notification.error({
1036 message: 'Failed to add property:' + error,
1040 }, () => !modal.instance.dynamicContent.instance.checkFormValidForSubmit()),
1041 new ButtonModel('Cancel', 'outline grey', () => {
1042 modal.instance.close();
1047 this.ModalService.addDynamicContentToModal(modal, PropertyCreatorComponent, {});
1048 modal.instance.open();
1051 /*** SEARCH RELATED FUNCTIONS ***/
1052 searchPropertiesInstances = (filterData:FilterPropertiesAssignmentData) => {
1053 let instanceBePropertiesMap:InstanceBePropertiesMap;
1054 this.componentServiceNg2
1055 .filterComponentInstanceProperties(this.component, filterData)
1056 .subscribe((response) => {
1057 this.processInstancePropertiesResponse(response, false);
1058 this.hierarchyPropertiesDisplayOptions.searchText = filterData.propertyName;//mark results in tree
1059 this.searchPropertyName = filterData.propertyName;//mark in table
1060 this.hierarchyNavTabs.triggerTabChange('Composition');
1061 this.propertiesNavigationData = [];
1062 this.displayClearSearch = true;
1063 }, (error) => {}); //ignore error
1067 clearSearch = () => {
1068 this.instancesNavigationData = this.instances;
1069 this.searchPropertyName = "";
1070 this.hierarchyPropertiesDisplayOptions.searchText = "";
1071 this.displayClearSearch = false;
1072 this.advanceSearch.clearAll();
1073 this.searchQuery = '';
1076 clickOnClearSearch = () => {
1078 this.selectFirstInstanceByDefault();
1079 this.hierarchyNavTabs.triggerTabChange('Composition');
1082 private isInput = (instanceType:string):boolean =>{
1083 return instanceType === ResourceType.VF || instanceType === ResourceType.PNF || instanceType === ResourceType.CVFC || instanceType === ResourceType.CR;