2 * ============LICENSE_START=======================================================
4 * ================================================================================
5 * Copyright (C) 2021 Nordix Foundation. 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 {Component, Inject, ViewChild} from '@angular/core';
24 Component as ComponentData,
29 import {SdcUiCommon, SdcUiServices} from 'onap-ui-angular';
30 import {TopologyTemplateService} from "../../services/component-services/topology-template.service";
31 import {Tab, Tabs} from "../../components/ui/tabs/tabs.component";
32 import * as _ from 'lodash';
33 import {OutputFEModel} from "../../../models/attributes-outputs/output-fe-model";
34 import {OutputBEModel} from "../../../models/attributes-outputs/output-be-model";
35 import {EVENTS, ResourceType, WorkspaceMode} from "../../../utils/constants";
36 import {ComponentModeService} from "../../services/component-services/component-mode.service";
37 import {EventListenerService} from "app/services";
38 import {HierarchyNavService} from "./services/hierarchy-nav.service";
39 import {ComponentServiceNg2} from "../../services/component-services/component.service";
40 import {ComponentInstanceServiceNg2} from "../../services/component-instance-services/component-instance.service";
41 import {KeysPipe} from "../../pipes/keys.pipe";
43 InstanceAttributesAPIMap,
44 InstanceBeAttributesMap,
45 InstanceFeAttributesMap
46 } from "app/models/attributes-outputs/attribute-fe-map";
48 InstanceBePropertiesMap
49 } from "app/models/properties-inputs/property-fe-map";
50 import {ModalService} from "../../services/modal.service";
51 import {InstanceFeDetails} from "../../../models/instance-fe-details";
52 import {HierarchyDisplayOptions} from "../../components/logic/hierarchy-navigtion/hierarchy-display-options";
53 import {UnsavedChangesComponent} from "../../components/ui/forms/unsaved-changes/unsaved-changes.component";
54 import {SimpleFlatAttribute} from "app/models/attributes-outputs/simple-flat-attribute";
55 import {AttributeFEModel} from "../../../models/attributes-outputs/attribute-fe-model";
56 import {AttributesUtils} from "./services/attributes.utils";
57 import {OutputsUtils} from "app/ng2/pages/attributes-outputs/services/outputs.utils";
58 import {AttributesService} from "app/ng2/services/attributes.service";
59 import {DerivedFEAttribute} from "../../../models/attributes-outputs/derived-fe-attribute";
60 import {AttributeBEModel} from "../../../models/attributes-outputs/attribute-be-model";
61 import {AttributeCreatorComponent} from "app/ng2/pages/attributes-outputs/attribute-creator/attribute-creator.component";
62 import {AttributeRowSelectedEvent} from "app/ng2/components/logic/attributes-table/attributes-table.component";
63 import { DeclareInputComponent } from '../properties-assignment/declare-input/declare-input.component';
65 const SERVICE_SELF_TITLE = "SELF";
68 selector: 'attributes-outputs',
69 templateUrl: './attributes-outputs.page.component.html',
70 styleUrls: ['./attributes-outputs.page.component.less', '../../../../assets/styles/table-style.less']
72 export class AttributesOutputsComponent {
73 title = "Attributes & Outputs";
75 @ViewChild('componentAttributesTable')
78 component: ComponentData;
79 componentInstanceNamesMap: Map<string, InstanceFeDetails> = new Map<string, InstanceFeDetails>();//instanceUniqueId, {name, iconClass}
81 attributesNavigationData = [];
82 instancesNavigationData = [];
84 instanceFeAttributesMap: InstanceFeAttributesMap;
85 outputs: Array<OutputFEModel> = [];
86 instances: Array<ComponentInstance> = [];
88 attributeStructureHeader: string;
90 selectedFlatAttribute: SimpleFlatAttribute = new SimpleFlatAttribute();
91 selectedInstanceData: ComponentInstance = null;
92 checkedAttributesCount: number = 0;
94 hierarchyAttributesDisplayOptions: HierarchyDisplayOptions = new HierarchyDisplayOptions('path', 'name', 'childrens');
95 hierarchyInstancesDisplayOptions: HierarchyDisplayOptions = new HierarchyDisplayOptions('uniqueId', 'name', 'archived', null, 'iconClass');
96 searchAttributeName: string;
98 isOutputsTabSelected: boolean;
99 isAttributesTabSelected: boolean;
101 resourceIsReadonly: boolean;
102 loadingInstances: boolean = false;
103 loadingOutputs: boolean = false;
104 loadingAttributes: boolean = false;
105 changedData: Array<AttributeFEModel | OutputFEModel>;
106 hasChangedData: boolean;
107 isValidChangedData: boolean;
108 savingChangedData: boolean;
109 stateChangeStartUnregister: Function;
110 serviceBeAttributesMap: InstanceBeAttributesMap;
112 @ViewChild('hierarchyNavTabs') hierarchyNavTabs: Tabs;
113 @ViewChild('attributeOutputTabs') attributeOutputTabs: Tabs;
117 private attributesService: AttributesService,
118 private hierarchyNavService: HierarchyNavService,
119 private attributesUtils: AttributesUtils,
120 private outputsUtils: OutputsUtils,
121 private componentServiceNg2: ComponentServiceNg2,
122 private componentInstanceServiceNg2: ComponentInstanceServiceNg2,
123 @Inject("$stateParams") _stateParams,
124 @Inject("$scope") private $scope: ng.IScope,
125 @Inject("$state") private $state: ng.ui.IStateService,
126 @Inject("Notification") private Notification: any,
127 private componentModeService: ComponentModeService,
128 private EventListenerService: EventListenerService,
129 private ModalServiceSdcUI: SdcUiServices.ModalService,
130 private ModalService: ModalService,
131 private keysPipe: KeysPipe,
132 private topologyTemplateService: TopologyTemplateService
135 this.instanceFeAttributesMap = new InstanceFeAttributesMap();
136 /* 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
137 than if the data is already exist, no need to call the api again - Ask orit if you have any questions*/
138 this.component = _stateParams.component;
139 this.EventListenerService.registerObserverCallback(EVENTS.ON_LIFECYCLE_CHANGE, this.onCheckout);
140 this.updateViewMode();
142 this.changedData = [];
143 this.updateHasChangedData();
144 this.isValidChangedData = true;
148 this.loadingOutputs = true;
149 this.loadingInstances = true;
150 this.loadingAttributes = true;
151 this.topologyTemplateService
152 .getComponentOutputsWithAttributes(this.component.componentType, this.component.uniqueId)
153 .subscribe(response => {
154 if (response.outputs) {
155 response.outputs.forEach(output => {
156 const newOutput: OutputFEModel = new OutputFEModel(output);
157 this.outputsUtils.resetOutputDefaultValue(newOutput, output.defaultValue);
158 this.outputs.push(newOutput);
162 this.Notification.error({
163 message: 'Failed to Initialize:' + error,
167 this.loadingOutputs = false;
169 this.componentServiceNg2
170 .getComponentResourceAttributesData(this.component)
171 .subscribe(response => {
173 this.instances.push(...response.componentInstances);
175 // add the service self instance to the top of the list.
176 const serviceInstance = new ComponentInstance();
177 serviceInstance.name = SERVICE_SELF_TITLE;
178 serviceInstance.uniqueId = this.component.uniqueId;
179 this.instances.unshift(serviceInstance);
180 if (this.instances) {
181 this.instances.forEach(instance => {
182 this.instancesNavigationData.push(instance);
183 this.componentInstanceNamesMap[instance.uniqueId] = <InstanceFeDetails>{
185 iconClass: instance.iconClass,
186 originArchived: instance.originArchived
190 this.selectFirstInstanceByDefault();
192 this.Notification.error({
193 message: 'Failed to Initialize:' + error,
197 this.loadingInstances = false;
198 this.loadingAttributes = false;
201 this.stateChangeStartUnregister = this.$scope.$on('$stateChangeStart', (event, toState, toParams) => {
202 // stop if has changed attributes
203 if (this.hasChangedData) {
204 event.preventDefault();
205 this.showUnsavedChangesAlert().then(() => {
206 this.$state.go(toState, toParams);
213 this.EventListenerService.unRegisterObserver(EVENTS.ON_LIFECYCLE_CHANGE);
214 this.stateChangeStartUnregister();
217 selectFirstInstanceByDefault = () => {
218 if (this.instancesNavigationData.length > 0) {
219 this.onInstanceSelectedUpdate(this.instancesNavigationData[0]);
223 updateViewMode = () => {
224 this.isReadonly = this.componentModeService.getComponentMode(this.component) === WorkspaceMode.VIEW;
227 onCheckout = (component: ComponentData) => {
228 this.component = component;
229 this.updateViewMode();
232 isSelf = (): boolean => {
233 return this.selectedInstanceData && this.selectedInstanceData.uniqueId == this.component.uniqueId;
236 getServiceAttributes() {
237 this.loadingAttributes = true;
238 this.topologyTemplateService
239 .getServiceAttributes(this.component.uniqueId)
240 .subscribe((response) => {
241 this.serviceBeAttributesMap = new InstanceBeAttributesMap();
242 this.serviceBeAttributesMap[this.component.uniqueId] = response;
243 this.processInstanceAttributesResponse(this.serviceBeAttributesMap, false, null);
245 this.Notification.error({
246 message: 'Failed to get Service Attribute:' + error,
250 this.loadingAttributes = false;
254 onInstanceSelectedUpdate = (instance: ComponentInstance) => {
255 // stop if has changed attributes
256 if (this.hasChangedData) {
257 this.showUnsavedChangesAlert().then(() => {
258 this.changeSelectedInstance(instance)
262 this.changeSelectedInstance(instance);
265 changeSelectedInstance = (instance: ComponentInstance) => {
266 this.selectedInstanceData = instance;
267 this.loadingAttributes = true;
268 this.instanceFeAttributesMap = undefined;
269 if (instance instanceof ComponentInstance) {
270 let instanceBeAttributesMap: InstanceBeAttributesMap | InstanceBePropertiesMap = new InstanceBeAttributesMap();
271 if (this.isOutput(instance.originType)) {
272 this.componentInstanceServiceNg2
273 .getComponentInstanceOutputs(this.component, instance)
274 .subscribe(response => {
275 instanceBeAttributesMap[instance.uniqueId] = response;
276 this.processInstanceAttributesResponse(instanceBeAttributesMap, true, instance.uniqueId);
278 this.Notification.error({
279 message: 'Failed to change Selected Instance:' + error,
283 this.loadingAttributes = false;
285 } else if (this.isSelf()) {
286 this.getServiceAttributes();
288 this.componentInstanceServiceNg2
289 .getComponentInstanceAttributes(this.component, instance.uniqueId)
290 .subscribe(response => {
291 instanceBeAttributesMap = new InstanceBeAttributesMap();
292 instanceBeAttributesMap[instance.uniqueId] = response;
293 this.processInstanceAttributesResponse(instanceBeAttributesMap, false, instance.uniqueId);
295 this.Notification.error({
296 message: 'Failed to change Selected Instance:' + error,
300 this.loadingAttributes = false;
302 this.componentInstanceServiceNg2
303 .getComponentInstanceProperties(this.component, instance.uniqueId)
304 .subscribe(response => {
305 instanceBeAttributesMap = new InstanceBePropertiesMap();
306 instanceBeAttributesMap[instance.uniqueId] = response;
307 this.processInstanceAttributesResponse(instanceBeAttributesMap, false, instance.uniqueId);
309 this.Notification.error({
310 message: 'Failed to change Selected Instance:' + error,
314 this.loadingAttributes = false;
317 this.resourceIsReadonly = (instance.componentName === "vnfConfiguration");
319 this.loadingAttributes = false;
322 //clear selected attribute from the navigation
323 this.selectedFlatAttribute = new SimpleFlatAttribute();
324 this.attributesNavigationData = [];
328 * Entry point handling response from server
330 processInstanceAttributesResponse = (instanceBeAttributesMap: InstanceBePropertiesMap | InstanceBeAttributesMap, originTypeIsVF: boolean, instanceId: string) => {
331 let attributesMap = this.attributesUtils.convertAttributesMapToFEAndCreateChildren(instanceBeAttributesMap, originTypeIsVF, this.outputs); //create flattened children, disable declared attribs, and init values
332 if (!this.instanceFeAttributesMap) {
333 this.instanceFeAttributesMap = attributesMap;
334 } else if (instanceId) {
335 let toAdd: AttributeFEModel[];
336 if (instanceBeAttributesMap instanceof InstanceBeAttributesMap) {
337 toAdd = this.instanceFeAttributesMap[instanceId].filter(currentAtr => !attributesMap[instanceId].some(newAtr => newAtr.name === currentAtr.name))
338 this.instanceFeAttributesMap[instanceId] = attributesMap[instanceId];
340 toAdd = attributesMap[instanceId].filter(currentAtr => !this.instanceFeAttributesMap[instanceId].some(newAtr => newAtr.name === currentAtr.name))
342 this.instanceFeAttributesMap[instanceId] = this.instanceFeAttributesMap[instanceId].concat(toAdd);
344 this.checkedAttributesCount = 0;
348 /*** VALUE CHANGE EVENTS ***/
349 dataChanged = (item: AttributeFEModel | OutputFEModel) => {
351 if (this.isAttributesTabSelected && item instanceof AttributeFEModel) {
352 itemHasChanged = item.hasValueObjChanged();
353 } else if (this.isOutputsTabSelected && item instanceof OutputFEModel) {
354 itemHasChanged = item.hasChanged();
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.isAttributesTabSelected) {
369 this.isValidChangedData = this.changedData.every((changedItem) => (<AttributeFEModel>changedItem).valueObjIsValid);
370 } else if (this.isOutputsTabSelected) {
371 this.isValidChangedData = this.changedData.every((changedItem) => (<OutputFEModel>changedItem).defaultValueObjIsValid);
373 this.updateHasChangedData();
377 /*** HEIRARCHY/NAV RELATED FUNCTIONS ***/
380 * Handle select node in navigation area, and select the row in table
382 onAttributeSelectedUpdate = ($event) => {
383 this.selectedFlatAttribute = $event;
384 let parentAttribute: AttributeFEModel = this.attributesService.getParentAttributeFEModelFromPath(this.instanceFeAttributesMap[this.selectedFlatAttribute.instanceName], this.selectedFlatAttribute.path);
385 parentAttribute.expandedChildAttributeId = this.selectedFlatAttribute.path;
389 * When user select row in table, this will prepare the hierarchy object for the tree.
391 selectAttributeRow = (attributeRowSelectedEvent: AttributeRowSelectedEvent) => {
392 let attribute = attributeRowSelectedEvent.attributeModel;
393 let instanceName = attributeRowSelectedEvent.instanceName;
394 this.attributeStructureHeader = null;
396 // Build hierarchy tree for the navigation and update attributesNavigationData with it.
397 if (!(this.selectedInstanceData instanceof ComponentInstance) || this.selectedInstanceData.originType !== ResourceType.VF) {
398 let simpleFlatAttributes: Array<SimpleFlatAttribute>;
399 if (attribute instanceof AttributeFEModel) {
400 simpleFlatAttributes = this.hierarchyNavService.getSimpleAttributesTree(attribute, instanceName);
401 } else if (attribute instanceof DerivedFEAttribute) {
402 // Need to find parent AttributeFEModel
403 let parentAttributeFEModel: AttributeFEModel = _.find(this.instanceFeAttributesMap[instanceName], (tmpFeAttribute): boolean => {
404 return attribute.attributesName.indexOf(tmpFeAttribute.name) === 0;
406 simpleFlatAttributes = this.hierarchyNavService.getSimpleAttributesTree(parentAttributeFEModel, instanceName);
408 this.attributesNavigationData = simpleFlatAttributes;
411 // Update the header in the navigation tree with attribute name.
412 this.attributeStructureHeader = (attribute.attributesName.split('#'))[0];
414 // Set selected attribute in table
415 this.selectedFlatAttribute = this.hierarchyNavService.createSimpleFlatAttribute(attribute, instanceName);
416 this.hierarchyNavTabs.triggerTabChange('Attribute Structure');
419 tabChanged = (event) => {
420 // stop if has changed attributes
421 if (this.hasChangedData) {
422 this.attributeOutputTabs.triggerTabChange(this.currentMainTab.title);
423 this.showUnsavedChangesAlert().then(() => {
424 this.attributeOutputTabs.selectTab(this.attributeOutputTabs.tabs.find((tab) => tab.title === event.title));
429 this.currentMainTab = this.attributeOutputTabs.tabs.find((tab) => tab.title === event.title);
430 this.isAttributesTabSelected = this.currentMainTab.title === "Attributes";
431 this.isOutputsTabSelected = this.currentMainTab.title === "Outputs";
432 this.attributeStructureHeader = null;
433 this.searchQuery = '';
436 declareOutput = (): void => {
437 if (this.checkedAttributesCount == 1) {
438 this.openAddOutputNameModal();
439 } else if (this.checkedAttributesCount > 1) {
440 this.declareAttributes();
444 private openAddOutputNameModal = (): void => {
445 const modalTitle = 'Enter name of the ouput to be created';
446 const modalButtons = [];
447 const modal = this.ModalService.createCustomModal(new ModalModel(
454 modalButtons.push(new ButtonModel('Save', 'blue',
456 const outputName: string = modal.instance.dynamicContent.instance.inputNameForm.value;
458 this.declareAttributes(outputName);
460 this.Notification.warning({
461 message: 'Failed to set input name',
465 this.ModalService.closeCurrentModal();
468 modalButtons.push(new ButtonModel('Cancel', 'outline grey', () => {
469 this.ModalService.closeCurrentModal();
471 this.ModalService.addDynamicContentToModal(modal, DeclareInputComponent, {defaultInputName: this.generateDefaultOutputName()});
472 modal.instance.open();
475 generateDefaultOutputName = (): string => {
476 let defaultInputName: string;
477 let instancesIds = this.keysPipe.transform(this.instanceFeAttributesMap, []);
478 angular.forEach(instancesIds, (instanceId: string) => {
479 const selectedOutput : AttributeBEModel = this.attributesService.getCheckedAttributes(this.instanceFeAttributesMap[instanceId])[0];
480 let selectedInstanceData: any = this.instances.find(instance => instance.uniqueId == instanceId);
481 defaultInputName = selectedOutput.name;
482 if (selectedInstanceData.invariantName) {
483 defaultInputName = selectedInstanceData.invariantName+'_'+selectedOutput.name;
486 return defaultInputName;
489 /*** DECLARE ATTRIBUTES/OUTPUTS ***/
490 declareAttributes = (outputName?: string): void => {
491 this.loadingAttributes = true;
492 let selectedComponentInstancesAttributes: InstanceBeAttributesMap = new InstanceBeAttributesMap();
493 let selectedComponentInstancesOutputs: InstanceBeAttributesMap = new InstanceBeAttributesMap();
494 let instancesIds = this.keysPipe.transform(this.instanceFeAttributesMap, []);
496 angular.forEach(instancesIds, (instanceId: string): void => {
497 let selectedInstanceData: any = this.instances.find(instance => instance.uniqueId == instanceId);
498 if (selectedInstanceData instanceof ComponentInstance) {
499 if (!this.isOutput(selectedInstanceData.originType)) {
500 // convert Attribute FE model -> Attribute BE model, extract only checked
501 selectedComponentInstancesAttributes[instanceId] = this.attributesService.getCheckedAttributes(this.instanceFeAttributesMap[instanceId]);
503 selectedComponentInstancesAttributes[instanceId][0].outputName = outputName;
506 selectedComponentInstancesOutputs[instanceId] = this.attributesService.getCheckedAttributes(this.instanceFeAttributesMap[instanceId]);
511 let outputsToCreate: InstanceAttributesAPIMap = new InstanceAttributesAPIMap(selectedComponentInstancesOutputs, selectedComponentInstancesAttributes);
512 this.topologyTemplateService
513 .createOutput(this.component, outputsToCreate, this.isSelf())
514 .subscribe((response) => {
515 this.setOutputTabIndication(response.length);
516 this.checkedAttributesCount = 0;
517 response.forEach((output: OutputBEModel) => {
518 const newOutput: OutputFEModel = new OutputFEModel(output);
519 this.outputsUtils.resetOutputDefaultValue(newOutput, output.defaultValue);
520 this.outputs.push(newOutput);
521 this.updateAttributeValueAfterDeclare(newOutput);
523 this.loadingAttributes = false;
524 }, error => {this.loadingAttributes = false;}); //ignore error
527 saveChangedData = (): Promise<(AttributeBEModel | OutputBEModel)[]> => {
528 return new Promise((resolve, reject) => {
529 if (!this.isValidChangedData) {
530 reject('Changed data is invalid - cannot save!');
533 if (!this.changedData.length) {
538 // make request and its handlers
540 let handleSuccess, handleError;
541 if (this.isAttributesTabSelected) {
542 const changedAttribs = this.changedData.map((changedAttrib) => {
543 changedAttrib = <AttributeFEModel>changedAttrib;
544 const attribBE = new AttributeBEModel(changedAttrib);
545 attribBE.toscaPresentation = new ToscaPresentationData();
546 attribBE.toscaPresentation.ownerId = changedAttrib.parentUniqueId;
547 attribBE.value = changedAttrib.getJSONValue();
548 attribBE.name = changedAttrib.origName || changedAttrib.name;
549 delete attribBE.origName;
553 if (this.selectedInstanceData instanceof ComponentInstance) {
555 console.log("changedAttribs", changedAttribs);
556 request = this.topologyTemplateService.updateServiceAttributes(this.component.uniqueId, _.map(changedAttribs, cp => {
557 delete cp.constraints;
561 request = this.componentInstanceServiceNg2
562 .updateInstanceAttributes(this.component.componentType, this.component.uniqueId, this.selectedInstanceData.uniqueId, changedAttribs);
564 handleSuccess = (response) => {
565 // reset each changed attribute with new value and remove it from changed attributes list
566 response.forEach((resAttrib) => {
567 const changedAttrib = <AttributeFEModel>this.changedData.shift();
568 this.attributesUtils.resetAttributeValue(changedAttrib, resAttrib.value);
571 console.log("updated instance attributes: ", response);
574 } else if (this.isOutputsTabSelected) {
575 const changedOutputs: OutputBEModel[] = this.changedData.map((changedOutput) => {
576 changedOutput = <OutputFEModel>changedOutput;
577 const outputBE = new OutputBEModel(changedOutput);
578 outputBE.defaultValue = changedOutput.getJSONDefaultValue();
581 request = this.componentServiceNg2.updateComponentOutputs(this.component, changedOutputs);
582 handleSuccess = (response) => {
583 // reset each changed attribute with new value and remove it from changed attributes list
584 response.forEach((resOutput) => {
585 const changedOutput = <OutputFEModel>this.changedData.shift();
586 this.outputsUtils.resetOutputDefaultValue(changedOutput, resOutput.defaultValue);
587 changedOutput.required = resOutput.required;
592 this.savingChangedData = true;
595 this.savingChangedData = false;
596 handleSuccess && handleSuccess(response);
597 this.updateHasChangedData();
601 this.savingChangedData = false;
602 handleError && handleError(error);
603 this.updateHasChangedData();
612 reverseChangedData = (): void => {
613 // make reverse item handler
614 let handleReverseItem;
615 if (this.isAttributesTabSelected) {
616 handleReverseItem = (changedItem) => {
617 changedItem = <AttributeFEModel>changedItem;
618 this.attributesUtils.resetAttributeValue(changedItem, changedItem.value);
619 this.checkedAttributesCount = 0;
621 } else if (this.isOutputsTabSelected) {
622 handleReverseItem = (changedItem) => {
623 changedItem = <OutputFEModel>changedItem;
624 this.outputsUtils.resetOutputDefaultValue(changedItem, changedItem.defaultValue);
625 changedItem.required = changedItem.requiredOrig;
629 this.changedData.forEach(handleReverseItem);
630 this.changedData = [];
631 this.updateHasChangedData();
634 updateHasChangedData = (): boolean => {
635 const curHasChangedData: boolean = (this.changedData.length > 0);
636 if (curHasChangedData !== this.hasChangedData) {
637 this.hasChangedData = curHasChangedData;
638 if (this.hasChangedData) {
639 this.EventListenerService.notifyObservers(EVENTS.ON_WORKSPACE_UNSAVED_CHANGES, this.hasChangedData, this.showUnsavedChangesAlert);
641 this.EventListenerService.notifyObservers(EVENTS.ON_WORKSPACE_UNSAVED_CHANGES, false);
644 return this.hasChangedData;
647 doSaveChangedData = (onSuccessFunction?: Function, onError?: Function): void => {
648 this.saveChangedData().then(
650 this.Notification.success({
651 message: 'Successfully saved changes',
654 if (onSuccessFunction) onSuccessFunction();
655 if (this.isAttributesTabSelected) {
656 this.checkedAttributesCount = 0;
660 this.Notification.error({
661 message: 'Failed to save changes!',
664 if (onError) onError();
669 showUnsavedChangesAlert = (): Promise<any> => {
670 let modalTitle: string;
671 if (this.isAttributesTabSelected) {
672 modalTitle = `Unsaved attributes for ${this.selectedInstanceData.name}`;
673 } else if (this.isOutputsTabSelected) {
674 modalTitle = `Unsaved outputs for ${this.component.name}`;
677 return new Promise<any>((resolve, reject) => {
678 this.ModalServiceSdcUI.openCustomModal(
682 type: SdcUiCommon.ModalType.custom,
683 testId: "navigate-modal",
689 type: SdcUiCommon.ButtonType.secondary,
692 callback: () => reject()
697 type: SdcUiCommon.ButtonType.secondary,
701 this.reverseChangedData();
708 type: SdcUiCommon.ButtonType.primary,
711 disabled: !this.isValidChangedData,
712 callback: () => this.doSaveChangedData(resolve, reject)
714 ] as SdcUiCommon.IModalButtonComponent[]
715 } as SdcUiCommon.IModalConfig, UnsavedChangesComponent, {isValidChangedData: this.isValidChangedData});
720 updateAttributeValueAfterDeclare = (output: OutputFEModel) => {
721 const attributeList = this.instanceFeAttributesMap[output.instanceUniqueId];
723 const instanceName = output.instanceUniqueId.slice(output.instanceUniqueId.lastIndexOf('.') + 1);
724 const attributeForUpdatingVal = attributeList.find((feAttribute: AttributeFEModel) => {
725 return feAttribute.name == output.relatedAttributeName &&
726 (feAttribute.name == output.relatedAttributeName || output.name === instanceName.concat('_').concat(feAttribute.name.replace(/[.]/g, '_')));
728 const outputPath = (output.outputPath && output.outputPath != attributeForUpdatingVal.name) ? output.outputPath : undefined;
729 attributeForUpdatingVal.setAsDeclared(outputPath); //set attribute as declared before assigning value
730 // this.attributesService.disableRelatedAttributes(attributeForUpdatingVal, outputPath);
731 this.attributesUtils.resetAttributeValue(attributeForUpdatingVal, output.relatedAttributeValue, outputPath);
735 //used for declare button, to keep count of newly checked attributes (and ignore declared attributes)
736 updateCheckedAttributeCount = (increment: boolean): void => {
737 this.checkedAttributesCount += (increment) ? 1 : -1;
740 setOutputTabIndication = (numOutputs: number): void => {
741 this.attributeOutputTabs.setTabIndication('Outputs', numOutputs);
744 deleteOutput = (output: OutputFEModel) => {
745 let outputToDelete = new OutputBEModel(output);
747 this.componentServiceNg2
748 .deleteOutput(this.component, outputToDelete)
749 .subscribe(response => {
750 this.outputs = this.outputs.filter(output => output.uniqueId !== response.uniqueId);
752 //Reload the whole instance for now - TODO: CHANGE THIS after the BE starts returning attributes within the response, use commented code below instead!
753 this.changeSelectedInstance(this.selectedInstanceData);
755 this.Notification.error({
756 message: 'Failed to delete Output:' + error,
762 deleteAttribute = (attribute: AttributeFEModel) => {
763 const attributeToDelete = new AttributeFEModel(attribute);
764 this.loadingAttributes = true;
765 const feMap = this.instanceFeAttributesMap;
766 this.topologyTemplateService
767 .deleteServiceAttribute(this.component.uniqueId, attributeToDelete)
768 .subscribe((response) => {
769 const attribs = feMap[this.component.uniqueId];
770 attribs.splice(attribs.findIndex(p => p.uniqueId === response), 1);
772 this.Notification.error({
773 message: 'Failed to delete Attribute:' + error,
777 this.loadingAttributes = false;
781 addAttribute = () => {
782 let modalTitle = 'Add Attribute';
783 let modal = this.ModalService.createCustomModal(new ModalModel(
788 new ButtonModel('Save', 'blue', () => {
789 modal.instance.dynamicContent.instance.isLoading = true;
790 const newAttribute: AttributeBEModel = modal.instance.dynamicContent.instance.attributeModel;
791 this.topologyTemplateService.createServiceAttribute(this.component.uniqueId, newAttribute)
792 .subscribe((response) => {
793 modal.instance.dynamicContent.instance.isLoading = false;
794 const newAttrib: AttributeFEModel = this.attributesUtils.convertAddAttributeBEToAttributeFE(response);
795 this.instanceFeAttributesMap[this.component.uniqueId].push(newAttrib);
796 modal.instance.close();
798 modal.instance.dynamicContent.instance.isLoading = false;
799 this.Notification.error({
800 message: 'Failed to add Attribute:' + error,
804 }, () => !modal.instance.dynamicContent.instance.checkFormValidForSubmit()),
805 new ButtonModel('Cancel', 'outline grey', () => {
806 modal.instance.close();
811 this.ModalService.addDynamicContentToModal(modal, AttributeCreatorComponent, {});
812 modal.instance.open();
815 private isOutput = (instanceType: string): boolean => {
816 return instanceType === ResourceType.VF || instanceType === ResourceType.PNF || instanceType === ResourceType.CVFC || instanceType === ResourceType.CR;