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";
47 import {ModalService} from "../../services/modal.service";
48 import {InstanceFeDetails} from "../../../models/instance-fe-details";
49 import {HierarchyDisplayOptions} from "../../components/logic/hierarchy-navigtion/hierarchy-display-options";
50 import {UnsavedChangesComponent} from "../../components/ui/forms/unsaved-changes/unsaved-changes.component";
51 import {SimpleFlatAttribute} from "app/models/attributes-outputs/simple-flat-attribute";
52 import {AttributeFEModel} from "../../../models/attributes-outputs/attribute-fe-model";
53 import {AttributesUtils} from "./services/attributes.utils";
54 import {OutputsUtils} from "app/ng2/pages/attributes-outputs/services/outputs.utils";
55 import {AttributesService} from "app/ng2/services/attributes.service";
56 import {DerivedFEAttribute} from "../../../models/attributes-outputs/derived-fe-attribute";
57 import {AttributeBEModel} from "../../../models/attributes-outputs/attribute-be-model";
58 import {AttributeCreatorComponent} from "app/ng2/pages/attributes-outputs/attribute-creator/attribute-creator.component";
59 import {AttributeRowSelectedEvent} from "app/ng2/components/logic/attributes-table/attributes-table.component";
61 const SERVICE_SELF_TITLE = "SELF";
64 selector: 'attributes-outputs',
65 templateUrl: './attributes-outputs.page.component.html',
66 styleUrls: ['./attributes-outputs.page.component.less', '../../../../assets/styles/table-style.less']
68 export class AttributesOutputsComponent {
69 title = "Attributes & Outputs";
71 @ViewChild('componentAttributesTable')
74 component: ComponentData;
75 componentInstanceNamesMap: Map<string, InstanceFeDetails> = new Map<string, InstanceFeDetails>();//instanceUniqueId, {name, iconClass}
77 attributesNavigationData = [];
78 instancesNavigationData = [];
80 instanceFeAttributesMap: InstanceFeAttributesMap;
81 outputs: Array<OutputFEModel> = [];
82 instances: Array<ComponentInstance> = [];
84 attributeStructureHeader: string;
86 selectedFlatAttribute: SimpleFlatAttribute = new SimpleFlatAttribute();
87 selectedInstanceData: ComponentInstance = null;
88 checkedAttributesCount: number = 0;
90 hierarchyAttributesDisplayOptions: HierarchyDisplayOptions = new HierarchyDisplayOptions('path', 'name', 'childrens');
91 hierarchyInstancesDisplayOptions: HierarchyDisplayOptions = new HierarchyDisplayOptions('uniqueId', 'name', 'archived', null, 'iconClass');
92 searchAttributeName: string;
94 isOutputsTabSelected: boolean;
95 isAttributesTabSelected: boolean;
97 resourceIsReadonly: boolean;
98 loadingInstances: boolean = false;
99 loadingOutputs: boolean = false;
100 loadingAttributes: boolean = false;
101 changedData: Array<AttributeFEModel | OutputFEModel>;
102 hasChangedData: boolean;
103 isValidChangedData: boolean;
104 savingChangedData: boolean;
105 stateChangeStartUnregister: Function;
106 serviceBeAttributesMap: InstanceBeAttributesMap;
108 @ViewChild('hierarchyNavTabs') hierarchyNavTabs: Tabs;
109 @ViewChild('attributeOutputTabs') attributeOutputTabs: Tabs;
112 constructor(private attributesService: AttributesService,
113 private hierarchyNavService: HierarchyNavService,
114 private attributesUtils: AttributesUtils,
115 private outputsUtils: OutputsUtils,
116 private componentServiceNg2: ComponentServiceNg2,
117 private componentInstanceServiceNg2: ComponentInstanceServiceNg2,
118 @Inject("$stateParams") _stateParams,
119 @Inject("$scope") private $scope: ng.IScope,
120 @Inject("$state") private $state: ng.ui.IStateService,
121 @Inject("Notification") private Notification: any,
122 private componentModeService: ComponentModeService,
123 private EventListenerService: EventListenerService,
124 private ModalServiceSdcUI: SdcUiServices.ModalService,
125 private ModalService: ModalService,
126 private keysPipe: KeysPipe,
127 private topologyTemplateService: TopologyTemplateService) {
129 this.instanceFeAttributesMap = new InstanceFeAttributesMap();
130 /* 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
131 than if the data is already exist, no need to call the api again - Ask orit if you have any questions*/
132 this.component = _stateParams.component;
133 this.EventListenerService.registerObserverCallback(EVENTS.ON_LIFECYCLE_CHANGE, this.onCheckout);
134 this.updateViewMode();
136 this.changedData = [];
137 this.updateHasChangedData();
138 this.isValidChangedData = true;
142 this.loadingOutputs = true;
143 this.loadingInstances = true;
144 this.loadingAttributes = true;
145 this.topologyTemplateService
146 .getComponentOutputsWithAttributes(this.component.componentType, this.component.uniqueId)
147 .subscribe(response => {
148 if (response.outputs) {
149 response.outputs.forEach(output => {
150 const newOutput: OutputFEModel = new OutputFEModel(output);
151 this.outputsUtils.resetOutputDefaultValue(newOutput, output.defaultValue);
152 this.outputs.push(newOutput);
156 this.Notification.error({
157 message: 'Failed to Initialize:' + error,
161 this.loadingOutputs = false;
163 this.componentServiceNg2
164 .getComponentResourceAttributesData(this.component)
165 .subscribe(response => {
167 this.instances.push(...response.componentInstances);
169 // add the service self instance to the top of the list.
170 const serviceInstance = new ComponentInstance();
171 serviceInstance.name = SERVICE_SELF_TITLE;
172 serviceInstance.uniqueId = this.component.uniqueId;
173 this.instances.unshift(serviceInstance);
174 if (this.instances) {
175 this.instances.forEach(instance => {
176 this.instancesNavigationData.push(instance);
177 this.componentInstanceNamesMap[instance.uniqueId] = <InstanceFeDetails>{
179 iconClass: instance.iconClass,
180 originArchived: instance.originArchived
184 this.selectFirstInstanceByDefault();
186 this.Notification.error({
187 message: 'Failed to Initialize:' + error,
191 this.loadingInstances = false;
192 this.loadingAttributes = false;
195 this.stateChangeStartUnregister = this.$scope.$on('$stateChangeStart', (event, toState, toParams) => {
196 // stop if has changed attributes
197 if (this.hasChangedData) {
198 event.preventDefault();
199 this.showUnsavedChangesAlert().then(() => {
200 this.$state.go(toState, toParams);
207 this.EventListenerService.unRegisterObserver(EVENTS.ON_LIFECYCLE_CHANGE);
208 this.stateChangeStartUnregister();
211 selectFirstInstanceByDefault = () => {
212 if (this.instancesNavigationData.length > 0) {
213 this.onInstanceSelectedUpdate(this.instancesNavigationData[0]);
217 updateViewMode = () => {
218 this.isReadonly = this.componentModeService.getComponentMode(this.component) === WorkspaceMode.VIEW;
221 onCheckout = (component: ComponentData) => {
222 this.component = component;
223 this.updateViewMode();
226 isSelf = (): boolean => {
227 return this.selectedInstanceData && this.selectedInstanceData.uniqueId == this.component.uniqueId;
230 getServiceAttributes() {
231 this.loadingAttributes = true;
232 this.topologyTemplateService
233 .getServiceAttributes(this.component.uniqueId)
234 .subscribe((response) => {
235 this.serviceBeAttributesMap = new InstanceBeAttributesMap();
236 this.serviceBeAttributesMap[this.component.uniqueId] = response;
237 this.processInstanceAttributesResponse(this.serviceBeAttributesMap, false);
239 this.Notification.error({
240 message: 'Failed to get Service Attribute:' + error,
244 this.loadingAttributes = false;
248 onInstanceSelectedUpdate = (instance: ComponentInstance) => {
249 // stop if has changed attributes
250 if (this.hasChangedData) {
251 this.showUnsavedChangesAlert().then(() => {
252 this.changeSelectedInstance(instance)
256 this.changeSelectedInstance(instance);
259 changeSelectedInstance = (instance: ComponentInstance) => {
260 this.selectedInstanceData = instance;
261 this.loadingAttributes = true;
262 if (instance instanceof ComponentInstance) {
263 let instanceBeAttributesMap: InstanceBeAttributesMap = new InstanceBeAttributesMap();
264 if (this.isOutput(instance.originType)) {
265 this.componentInstanceServiceNg2
266 .getComponentInstanceOutputs(this.component, instance)
267 .subscribe(response => {
268 instanceBeAttributesMap[instance.uniqueId] = response;
269 this.processInstanceAttributesResponse(instanceBeAttributesMap, true);
271 this.Notification.error({
272 message: 'Failed to change Selected Instance:' + error,
276 this.loadingAttributes = false;
278 } else if (this.isSelf()) {
279 this.getServiceAttributes();
281 this.componentInstanceServiceNg2
282 .getComponentInstanceAttributes(this.component, instance.uniqueId)
283 .subscribe(response => {
284 instanceBeAttributesMap[instance.uniqueId] = response;
285 this.processInstanceAttributesResponse(instanceBeAttributesMap, false);
287 this.Notification.error({
288 message: 'Failed to change Selected Instance:' + error,
292 this.loadingAttributes = false;
296 this.resourceIsReadonly = (instance.componentName === "vnfConfiguration");
298 this.loadingAttributes = false;
301 //clear selected attribute from the navigation
302 this.selectedFlatAttribute = new SimpleFlatAttribute();
303 this.attributesNavigationData = [];
307 * Entry point handling response from server
309 processInstanceAttributesResponse = (instanceBeAttributesMap: InstanceBeAttributesMap, originTypeIsVF: boolean) => {
310 this.instanceFeAttributesMap = this.attributesUtils.convertAttributesMapToFEAndCreateChildren(instanceBeAttributesMap, originTypeIsVF, this.outputs); //create flattened children, disable declared attribs, and init values
311 this.checkedAttributesCount = 0;
315 /*** VALUE CHANGE EVENTS ***/
316 dataChanged = (item: AttributeFEModel | OutputFEModel) => {
318 if (this.isAttributesTabSelected && item instanceof AttributeFEModel) {
319 itemHasChanged = item.hasValueObjChanged();
320 } else if (this.isOutputsTabSelected && item instanceof OutputFEModel) {
321 itemHasChanged = item.hasChanged();
324 const dataChangedIdx = this.changedData.findIndex((changedItem) => changedItem === item);
325 if (itemHasChanged) {
326 if (dataChangedIdx === -1) {
327 this.changedData.push(item);
330 if (dataChangedIdx !== -1) {
331 this.changedData.splice(dataChangedIdx, 1);
335 if (this.isAttributesTabSelected) {
336 this.isValidChangedData = this.changedData.every((changedItem) => (<AttributeFEModel>changedItem).valueObjIsValid);
337 } else if (this.isOutputsTabSelected) {
338 this.isValidChangedData = this.changedData.every((changedItem) => (<OutputFEModel>changedItem).defaultValueObjIsValid);
340 this.updateHasChangedData();
344 /*** HEIRARCHY/NAV RELATED FUNCTIONS ***/
347 * Handle select node in navigation area, and select the row in table
349 onAttributeSelectedUpdate = ($event) => {
350 this.selectedFlatAttribute = $event;
351 let parentAttribute: AttributeFEModel = this.attributesService.getParentAttributeFEModelFromPath(this.instanceFeAttributesMap[this.selectedFlatAttribute.instanceName], this.selectedFlatAttribute.path);
352 parentAttribute.expandedChildAttributeId = this.selectedFlatAttribute.path;
356 * When user select row in table, this will prepare the hierarchy object for the tree.
358 selectAttributeRow = (attributeRowSelectedEvent: AttributeRowSelectedEvent) => {
359 let attribute = attributeRowSelectedEvent.attributeModel;
360 let instanceName = attributeRowSelectedEvent.instanceName;
361 this.attributeStructureHeader = null;
363 // Build hierarchy tree for the navigation and update attributesNavigationData with it.
364 if (!(this.selectedInstanceData instanceof ComponentInstance) || this.selectedInstanceData.originType !== ResourceType.VF) {
365 let simpleFlatAttributes: Array<SimpleFlatAttribute>;
366 if (attribute instanceof AttributeFEModel) {
367 simpleFlatAttributes = this.hierarchyNavService.getSimpleAttributesTree(attribute, instanceName);
368 } else if (attribute instanceof DerivedFEAttribute) {
369 // Need to find parent AttributeFEModel
370 let parentAttributeFEModel: AttributeFEModel = _.find(this.instanceFeAttributesMap[instanceName], (tmpFeAttribute): boolean => {
371 return attribute.attributesName.indexOf(tmpFeAttribute.name) === 0;
373 simpleFlatAttributes = this.hierarchyNavService.getSimpleAttributesTree(parentAttributeFEModel, instanceName);
375 this.attributesNavigationData = simpleFlatAttributes;
378 // Update the header in the navigation tree with attribute name.
379 this.attributeStructureHeader = (attribute.attributesName.split('#'))[0];
381 // Set selected attribute in table
382 this.selectedFlatAttribute = this.hierarchyNavService.createSimpleFlatAttribute(attribute, instanceName);
383 this.hierarchyNavTabs.triggerTabChange('Attribute Structure');
386 tabChanged = (event) => {
387 // stop if has changed attributes
388 if (this.hasChangedData) {
389 this.attributeOutputTabs.triggerTabChange(this.currentMainTab.title);
390 this.showUnsavedChangesAlert().then(() => {
391 this.attributeOutputTabs.selectTab(this.attributeOutputTabs.tabs.find((tab) => tab.title === event.title));
396 this.currentMainTab = this.attributeOutputTabs.tabs.find((tab) => tab.title === event.title);
397 this.isAttributesTabSelected = this.currentMainTab.title === "Attributes";
398 this.isOutputsTabSelected = this.currentMainTab.title === "Outputs";
399 this.attributeStructureHeader = null;
400 this.searchQuery = '';
404 /*** DECLARE ATTRIBUTES/OUTPUTS ***/
405 declareAttributes = (): void => {
406 let selectedComponentInstancesAttributes: InstanceBeAttributesMap = new InstanceBeAttributesMap();
407 let selectedComponentInstancesOutputs: InstanceBeAttributesMap = new InstanceBeAttributesMap();
408 let instancesIds = this.keysPipe.transform(this.instanceFeAttributesMap, []);
410 angular.forEach(instancesIds, (instanceId: string): void => {
411 let selectedInstanceData: any = this.instances.find(instance => instance.uniqueId == instanceId);
412 if (selectedInstanceData instanceof ComponentInstance) {
413 if (!this.isOutput(selectedInstanceData.originType)) {
414 // convert Attribute FE model -> Attribute BE model, extract only checked
415 selectedComponentInstancesAttributes[instanceId] = this.attributesService.getCheckedAttributes(this.instanceFeAttributesMap[instanceId]);
417 selectedComponentInstancesOutputs[instanceId] = this.attributesService.getCheckedAttributes(this.instanceFeAttributesMap[instanceId]);
422 let outputsToCreate: InstanceAttributesAPIMap = new InstanceAttributesAPIMap(selectedComponentInstancesOutputs, selectedComponentInstancesAttributes);
423 this.topologyTemplateService
424 .createOutput(this.component, outputsToCreate, this.isSelf())
425 .subscribe((response) => {
426 this.setOutputTabIndication(response.length);
427 this.checkedAttributesCount = 0;
428 response.forEach((output: OutputBEModel) => {
429 const newOutput: OutputFEModel = new OutputFEModel(output);
430 this.outputsUtils.resetOutputDefaultValue(newOutput, output.defaultValue);
431 this.outputs.push(newOutput);
432 this.updateAttributeValueAfterDeclare(newOutput);
437 saveChangedData = (): Promise<(AttributeBEModel | OutputBEModel)[]> => {
438 return new Promise((resolve, reject) => {
439 if (!this.isValidChangedData) {
440 reject('Changed data is invalid - cannot save!');
443 if (!this.changedData.length) {
448 // make request and its handlers
450 let handleSuccess, handleError;
451 if (this.isAttributesTabSelected) {
452 const changedAttribs = this.changedData.map((changedAttrib) => {
453 changedAttrib = <AttributeFEModel>changedAttrib;
454 const attribBE = new AttributeBEModel(changedAttrib);
455 attribBE.toscaPresentation = new ToscaPresentationData();
456 attribBE.toscaPresentation.ownerId = changedAttrib.parentUniqueId;
457 attribBE.value = changedAttrib.getJSONValue();
458 attribBE.name = changedAttrib.origName || changedAttrib.name;
459 delete attribBE.origName;
463 if (this.selectedInstanceData instanceof ComponentInstance) {
465 console.log("changedAttribs", changedAttribs);
466 request = this.topologyTemplateService.updateServiceAttributes(this.component.uniqueId, _.map(changedAttribs, cp => {
467 delete cp.constraints;
471 request = this.componentInstanceServiceNg2
472 .updateInstanceAttributes(this.component.componentType, this.component.uniqueId, this.selectedInstanceData.uniqueId, changedAttribs);
474 handleSuccess = (response) => {
475 // reset each changed attribute with new value and remove it from changed attributes list
476 response.forEach((resAttrib) => {
477 const changedAttrib = <AttributeFEModel>this.changedData.shift();
478 this.attributesUtils.resetAttributeValue(changedAttrib, resAttrib.value);
481 console.log("updated instance attributes: ", response);
484 } else if (this.isOutputsTabSelected) {
485 const changedOutputs: OutputBEModel[] = this.changedData.map((changedOutput) => {
486 changedOutput = <OutputFEModel>changedOutput;
487 const outputBE = new OutputBEModel(changedOutput);
488 outputBE.defaultValue = changedOutput.getJSONDefaultValue();
491 request = this.componentServiceNg2.updateComponentOutputs(this.component, changedOutputs);
492 handleSuccess = (response) => {
493 // reset each changed attribute with new value and remove it from changed attributes list
494 response.forEach((resOutput) => {
495 const changedOutput = <OutputFEModel>this.changedData.shift();
496 this.outputsUtils.resetOutputDefaultValue(changedOutput, resOutput.defaultValue);
497 changedOutput.required = resOutput.required;
502 this.savingChangedData = true;
505 this.savingChangedData = false;
506 handleSuccess && handleSuccess(response);
507 this.updateHasChangedData();
511 this.savingChangedData = false;
512 handleError && handleError(error);
513 this.updateHasChangedData();
522 reverseChangedData = (): void => {
523 // make reverse item handler
524 let handleReverseItem;
525 if (this.isAttributesTabSelected) {
526 handleReverseItem = (changedItem) => {
527 changedItem = <AttributeFEModel>changedItem;
528 this.attributesUtils.resetAttributeValue(changedItem, changedItem.value);
529 this.checkedAttributesCount = 0;
531 } else if (this.isOutputsTabSelected) {
532 handleReverseItem = (changedItem) => {
533 changedItem = <OutputFEModel>changedItem;
534 this.outputsUtils.resetOutputDefaultValue(changedItem, changedItem.defaultValue);
535 changedItem.required = changedItem.requiredOrig;
539 this.changedData.forEach(handleReverseItem);
540 this.changedData = [];
541 this.updateHasChangedData();
544 updateHasChangedData = (): boolean => {
545 const curHasChangedData: boolean = (this.changedData.length > 0);
546 if (curHasChangedData !== this.hasChangedData) {
547 this.hasChangedData = curHasChangedData;
548 if (this.hasChangedData) {
549 this.EventListenerService.notifyObservers(EVENTS.ON_WORKSPACE_UNSAVED_CHANGES, this.hasChangedData, this.showUnsavedChangesAlert);
551 this.EventListenerService.notifyObservers(EVENTS.ON_WORKSPACE_UNSAVED_CHANGES, false);
554 return this.hasChangedData;
557 doSaveChangedData = (onSuccessFunction?: Function, onError?: Function): void => {
558 this.saveChangedData().then(
560 this.Notification.success({
561 message: 'Successfully saved changes',
564 if (onSuccessFunction) onSuccessFunction();
565 if (this.isAttributesTabSelected) {
566 this.checkedAttributesCount = 0;
570 this.Notification.error({
571 message: 'Failed to save changes!',
574 if (onError) onError();
579 showUnsavedChangesAlert = (): Promise<any> => {
580 let modalTitle: string;
581 if (this.isAttributesTabSelected) {
582 modalTitle = `Unsaved attributes for ${this.selectedInstanceData.name}`;
583 } else if (this.isOutputsTabSelected) {
584 modalTitle = `Unsaved outputs for ${this.component.name}`;
587 return new Promise<any>((resolve, reject) => {
588 this.ModalServiceSdcUI.openCustomModal(
592 type: SdcUiCommon.ModalType.custom,
593 testId: "navigate-modal",
599 type: SdcUiCommon.ButtonType.secondary,
602 callback: () => reject()
607 type: SdcUiCommon.ButtonType.secondary,
611 this.reverseChangedData();
618 type: SdcUiCommon.ButtonType.primary,
621 disabled: !this.isValidChangedData,
622 callback: () => this.doSaveChangedData(resolve, reject)
624 ] as SdcUiCommon.IModalButtonComponent[]
625 } as SdcUiCommon.IModalConfig, UnsavedChangesComponent, {isValidChangedData: this.isValidChangedData});
630 updateAttributeValueAfterDeclare = (output: OutputFEModel) => {
631 const attributeList = this.instanceFeAttributesMap[output.instanceUniqueId];
633 const instanceName = output.instanceUniqueId.slice(output.instanceUniqueId.lastIndexOf('.') + 1);
634 const attributeForUpdatingVal = attributeList.find((feAttribute: AttributeFEModel) => {
635 return feAttribute.name == output.relatedAttributeName &&
636 (feAttribute.name == output.relatedAttributeName || output.name === instanceName.concat('_').concat(feAttribute.name.replace(/[.]/g, '_')));
638 const outputPath = (output.outputPath && output.outputPath != attributeForUpdatingVal.name) ? output.outputPath : undefined;
639 attributeForUpdatingVal.setAsDeclared(outputPath); //set attribute as declared before assigning value
640 // this.attributesService.disableRelatedAttributes(attributeForUpdatingVal, outputPath);
641 this.attributesUtils.resetAttributeValue(attributeForUpdatingVal, output.relatedAttributeValue, outputPath);
645 //used for declare button, to keep count of newly checked attributes (and ignore declared attributes)
646 updateCheckedAttributeCount = (increment: boolean): void => {
647 this.checkedAttributesCount += (increment) ? 1 : -1;
650 setOutputTabIndication = (numOutputs: number): void => {
651 this.attributeOutputTabs.setTabIndication('Outputs', numOutputs);
654 deleteOutput = (output: OutputFEModel) => {
655 let outputToDelete = new OutputBEModel(output);
657 this.componentServiceNg2
658 .deleteOutput(this.component, outputToDelete)
659 .subscribe(response => {
660 this.outputs = this.outputs.filter(output => output.uniqueId !== response.uniqueId);
662 //Reload the whole instance for now - TODO: CHANGE THIS after the BE starts returning attributes within the response, use commented code below instead!
663 this.changeSelectedInstance(this.selectedInstanceData);
665 this.Notification.error({
666 message: 'Failed to delete Output:' + error,
672 deleteAttribute = (attribute: AttributeFEModel) => {
673 const attributeToDelete = new AttributeFEModel(attribute);
674 this.loadingAttributes = true;
675 const feMap = this.instanceFeAttributesMap;
676 this.topologyTemplateService
677 .deleteServiceAttribute(this.component.uniqueId, attributeToDelete)
678 .subscribe((response) => {
679 const attribs = feMap[this.component.uniqueId];
680 attribs.splice(attribs.findIndex(p => p.uniqueId === response), 1);
682 this.Notification.error({
683 message: 'Failed to delete Attribute:' + error,
687 this.loadingAttributes = false;
691 addAttribute = () => {
692 let modalTitle = 'Add Attribute';
693 let modal = this.ModalService.createCustomModal(new ModalModel(
698 new ButtonModel('Save', 'blue', () => {
699 modal.instance.dynamicContent.instance.isLoading = true;
700 const newAttribute: AttributeBEModel = modal.instance.dynamicContent.instance.attributeModel;
701 this.topologyTemplateService.createServiceAttribute(this.component.uniqueId, newAttribute)
702 .subscribe((response) => {
703 modal.instance.dynamicContent.instance.isLoading = false;
704 const newAttrib: AttributeFEModel = this.attributesUtils.convertAddAttributeBEToAttributeFE(response);
705 this.instanceFeAttributesMap[this.component.uniqueId].push(newAttrib);
706 modal.instance.close();
708 modal.instance.dynamicContent.instance.isLoading = false;
709 this.Notification.error({
710 message: 'Failed to add Attribute:' + error,
714 }, () => !modal.instance.dynamicContent.instance.checkFormValidForSubmit()),
715 new ButtonModel('Cancel', 'outline grey', () => {
716 modal.instance.close();
721 this.ModalService.addDynamicContentToModal(modal, AttributeCreatorComponent, {});
722 modal.instance.open();
725 private isOutput = (instanceType: string): boolean => {
726 return instanceType === ResourceType.VF || instanceType === ResourceType.PNF || instanceType === ResourceType.CVFC || instanceType === ResourceType.CR;