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);
208 this.EventListenerService.unRegisterObserver(EVENTS.ON_LIFECYCLE_CHANGE);
209 this.stateChangeStartUnregister();
212 selectFirstInstanceByDefault = () => {
213 if (this.instancesNavigationData.length > 0) {
214 this.onInstanceSelectedUpdate(this.instancesNavigationData[0]);
218 updateViewMode = () => {
219 this.isReadonly = this.componentModeService.getComponentMode(this.component) === WorkspaceMode.VIEW;
222 onCheckout = (component: ComponentData) => {
223 this.component = component;
224 this.updateViewMode();
227 isSelf = (): boolean => {
228 return this.selectedInstanceData && this.selectedInstanceData.uniqueId == this.component.uniqueId;
231 getServiceAttributes() {
232 this.loadingAttributes = true;
233 this.topologyTemplateService
234 .getServiceAttributes(this.component.uniqueId)
235 .subscribe((response) => {
236 this.serviceBeAttributesMap = new InstanceBeAttributesMap();
237 this.serviceBeAttributesMap[this.component.uniqueId] = response;
238 this.processInstanceAttributesResponse(this.serviceBeAttributesMap, false);
240 this.Notification.error({
241 message: 'Failed to get Service Attribute:' + error,
245 this.loadingAttributes = false;
249 onInstanceSelectedUpdate = (instance: ComponentInstance) => {
250 // stop if has changed attributes
251 if (this.hasChangedData) {
252 this.showUnsavedChangesAlert().then(() => {
253 this.changeSelectedInstance(instance)
257 this.changeSelectedInstance(instance);
260 changeSelectedInstance = (instance: ComponentInstance) => {
261 this.selectedInstanceData = instance;
262 this.loadingAttributes = true;
263 if (instance instanceof ComponentInstance) {
264 let instanceBeAttributesMap: InstanceBeAttributesMap = new InstanceBeAttributesMap();
265 if (this.isOutput(instance.originType)) {
266 this.componentInstanceServiceNg2
267 .getComponentInstanceOutputs(this.component, instance)
268 .subscribe(response => {
269 instanceBeAttributesMap[instance.uniqueId] = response;
270 this.processInstanceAttributesResponse(instanceBeAttributesMap, true);
272 this.Notification.error({
273 message: 'Failed to change Selected Instance:' + error,
277 this.loadingAttributes = false;
279 } else if (this.isSelf()) {
280 this.getServiceAttributes();
282 this.componentInstanceServiceNg2
283 .getComponentInstanceAttributes(this.component, instance.uniqueId)
284 .subscribe(response => {
285 instanceBeAttributesMap[instance.uniqueId] = response;
286 this.processInstanceAttributesResponse(instanceBeAttributesMap, false);
288 this.Notification.error({
289 message: 'Failed to change Selected Instance:' + error,
293 this.loadingAttributes = false;
297 this.resourceIsReadonly = (instance.componentName === "vnfConfiguration");
299 this.loadingAttributes = false;
302 //clear selected attribute from the navigation
303 this.selectedFlatAttribute = new SimpleFlatAttribute();
304 this.attributesNavigationData = [];
308 * Entry point handling response from server
310 processInstanceAttributesResponse = (instanceBeAttributesMap: InstanceBeAttributesMap, originTypeIsVF: boolean) => {
311 this.instanceFeAttributesMap = this.attributesUtils.convertAttributesMapToFEAndCreateChildren(instanceBeAttributesMap, originTypeIsVF, this.outputs); //create flattened children, disable declared attribs, and init values
312 this.checkedAttributesCount = 0;
316 /*** VALUE CHANGE EVENTS ***/
317 dataChanged = (item: AttributeFEModel | OutputFEModel) => {
319 if (this.isAttributesTabSelected && item instanceof AttributeFEModel) {
320 itemHasChanged = item.hasValueObjChanged();
321 } else if (this.isOutputsTabSelected && item instanceof OutputFEModel) {
322 itemHasChanged = item.hasChanged();
325 const dataChangedIdx = this.changedData.findIndex((changedItem) => changedItem === item);
326 if (itemHasChanged) {
327 if (dataChangedIdx === -1) {
328 this.changedData.push(item);
331 if (dataChangedIdx !== -1) {
332 this.changedData.splice(dataChangedIdx, 1);
336 if (this.isAttributesTabSelected) {
337 this.isValidChangedData = this.changedData.every((changedItem) => (<AttributeFEModel>changedItem).valueObjIsValid);
338 } else if (this.isOutputsTabSelected) {
339 this.isValidChangedData = this.changedData.every((changedItem) => (<OutputFEModel>changedItem).defaultValueObjIsValid);
341 this.updateHasChangedData();
345 /*** HEIRARCHY/NAV RELATED FUNCTIONS ***/
348 * Handle select node in navigation area, and select the row in table
350 onAttributeSelectedUpdate = ($event) => {
351 this.selectedFlatAttribute = $event;
352 let parentAttribute: AttributeFEModel = this.attributesService.getParentAttributeFEModelFromPath(this.instanceFeAttributesMap[this.selectedFlatAttribute.instanceName], this.selectedFlatAttribute.path);
353 parentAttribute.expandedChildAttributeId = this.selectedFlatAttribute.path;
357 * When user select row in table, this will prepare the hierarchy object for the tree.
359 selectAttributeRow = (attributeRowSelectedEvent: AttributeRowSelectedEvent) => {
360 let attribute = attributeRowSelectedEvent.attributeModel;
361 let instanceName = attributeRowSelectedEvent.instanceName;
362 this.attributeStructureHeader = null;
364 // Build hierarchy tree for the navigation and update attributesNavigationData with it.
365 if (!(this.selectedInstanceData instanceof ComponentInstance) || this.selectedInstanceData.originType !== ResourceType.VF) {
366 let simpleFlatAttributes: Array<SimpleFlatAttribute>;
367 if (attribute instanceof AttributeFEModel) {
368 simpleFlatAttributes = this.hierarchyNavService.getSimpleAttributesTree(attribute, instanceName);
369 } else if (attribute instanceof DerivedFEAttribute) {
370 // Need to find parent AttributeFEModel
371 let parentAttributeFEModel: AttributeFEModel = _.find(this.instanceFeAttributesMap[instanceName], (tmpFeAttribute): boolean => {
372 return attribute.attributesName.indexOf(tmpFeAttribute.name) === 0;
374 simpleFlatAttributes = this.hierarchyNavService.getSimpleAttributesTree(parentAttributeFEModel, instanceName);
376 this.attributesNavigationData = simpleFlatAttributes;
379 // Update the header in the navigation tree with attribute name.
380 this.attributeStructureHeader = (attribute.attributesName.split('#'))[0];
382 // Set selected attribute in table
383 this.selectedFlatAttribute = this.hierarchyNavService.createSimpleFlatAttribute(attribute, instanceName);
384 this.hierarchyNavTabs.triggerTabChange('Attribute Structure');
387 tabChanged = (event) => {
388 // stop if has changed attributes
389 if (this.hasChangedData) {
390 this.attributeOutputTabs.triggerTabChange(this.currentMainTab.title);
391 this.showUnsavedChangesAlert().then(() => {
392 this.attributeOutputTabs.selectTab(this.attributeOutputTabs.tabs.find((tab) => tab.title === event.title));
398 this.currentMainTab = this.attributeOutputTabs.tabs.find((tab) => tab.title === event.title);
399 this.isAttributesTabSelected = this.currentMainTab.title === "Attributes";
400 this.isOutputsTabSelected = this.currentMainTab.title === "Outputs";
401 this.attributeStructureHeader = null;
402 this.searchQuery = '';
406 /*** DECLARE ATTRIBUTES/OUTPUTS ***/
407 declareAttributes = (): void => {
408 let selectedComponentInstancesAttributes: InstanceBeAttributesMap = new InstanceBeAttributesMap();
409 let selectedComponentInstancesOutputs: InstanceBeAttributesMap = new InstanceBeAttributesMap();
410 let instancesIds = this.keysPipe.transform(this.instanceFeAttributesMap, []);
412 angular.forEach(instancesIds, (instanceId: string): void => {
413 let selectedInstanceData: any = this.instances.find(instance => instance.uniqueId == instanceId);
414 if (selectedInstanceData instanceof ComponentInstance) {
415 if (!this.isOutput(selectedInstanceData.originType)) {
416 // convert Attribute FE model -> Attribute BE model, extract only checked
417 selectedComponentInstancesAttributes[instanceId] = this.attributesService.getCheckedAttributes(this.instanceFeAttributesMap[instanceId]);
419 selectedComponentInstancesOutputs[instanceId] = this.attributesService.getCheckedAttributes(this.instanceFeAttributesMap[instanceId]);
424 let outputsToCreate: InstanceAttributesAPIMap = new InstanceAttributesAPIMap(selectedComponentInstancesOutputs, selectedComponentInstancesAttributes);
425 this.topologyTemplateService
426 .createOutput(this.component, outputsToCreate, this.isSelf())
427 .subscribe((response) => {
428 this.setOutputTabIndication(response.length);
429 this.checkedAttributesCount = 0;
430 response.forEach((output: OutputBEModel) => {
431 const newOutput: OutputFEModel = new OutputFEModel(output);
432 this.outputsUtils.resetOutputDefaultValue(newOutput, output.defaultValue);
433 this.outputs.push(newOutput);
434 this.updateAttributeValueAfterDeclare(newOutput);
439 saveChangedData = (): Promise<(AttributeBEModel | OutputBEModel)[]> => {
440 return new Promise((resolve, reject) => {
441 if (!this.isValidChangedData) {
442 reject('Changed data is invalid - cannot save!');
445 if (!this.changedData.length) {
450 // make request and its handlers
452 let handleSuccess, handleError;
453 if (this.isAttributesTabSelected) {
454 this.changedData.map((changedAttrib) => {
455 changedAttrib = <AttributeFEModel>changedAttrib;
456 const attribBE = new AttributeBEModel(changedAttrib);
457 attribBE.toscaPresentation = new ToscaPresentationData();
458 attribBE.toscaPresentation.ownerId = changedAttrib.parentUniqueId;
459 attribBE.value = changedAttrib.getJSONValue();
460 attribBE.name = changedAttrib.origName || changedAttrib.name;
461 delete attribBE.origName;
464 } else if (this.isOutputsTabSelected) {
465 const changedOutputs: OutputBEModel[] = this.changedData.map((changedOutput) => {
466 changedOutput = <OutputFEModel>changedOutput;
467 const outputBE = new OutputBEModel(changedOutput);
468 outputBE.defaultValue = changedOutput.getJSONDefaultValue();
471 request = this.componentServiceNg2
472 .updateComponentOutputs(this.component, changedOutputs);
473 handleSuccess = (response) => {
474 // reset each changed attribute with new value and remove it from changed attributes list
475 response.forEach((resOutput) => {
476 const changedOutput = <OutputFEModel>this.changedData.shift();
477 this.outputsUtils.resetOutputDefaultValue(changedOutput, resOutput.defaultValue);
478 changedOutput.required = resOutput.required;
481 this.savingChangedData = true;
484 this.savingChangedData = false;
485 handleSuccess && handleSuccess(response);
486 this.updateHasChangedData();
490 this.savingChangedData = false;
491 handleError && handleError(error);
492 this.updateHasChangedData();
502 reverseChangedData = (): void => {
503 // make reverse item handler
504 let handleReverseItem;
505 if (this.isAttributesTabSelected) {
506 handleReverseItem = (changedItem) => {
507 changedItem = <AttributeFEModel>changedItem;
508 this.attributesUtils.resetAttributeValue(changedItem, changedItem.value);
509 this.checkedAttributesCount = 0;
511 } else if (this.isOutputsTabSelected) {
512 handleReverseItem = (changedItem) => {
513 changedItem = <OutputFEModel>changedItem;
514 this.outputsUtils.resetOutputDefaultValue(changedItem, changedItem.defaultValue);
515 changedItem.required = changedItem.requiredOrig;
519 this.changedData.forEach(handleReverseItem);
520 this.changedData = [];
521 this.updateHasChangedData();
524 updateHasChangedData = (): boolean => {
525 const curHasChangedData: boolean = (this.changedData.length > 0);
526 if (curHasChangedData !== this.hasChangedData) {
527 this.hasChangedData = curHasChangedData;
528 if (this.hasChangedData) {
529 this.EventListenerService.notifyObservers(EVENTS.ON_WORKSPACE_UNSAVED_CHANGES, this.hasChangedData, this.showUnsavedChangesAlert);
531 this.EventListenerService.notifyObservers(EVENTS.ON_WORKSPACE_UNSAVED_CHANGES, false);
534 return this.hasChangedData;
537 doSaveChangedData = (onSuccessFunction?: Function, onError?: Function): void => {
538 this.saveChangedData().then(
540 this.Notification.success({
541 message: 'Successfully saved changes',
544 if (onSuccessFunction) onSuccessFunction();
545 if (this.isAttributesTabSelected) {
546 this.checkedAttributesCount = 0;
550 this.Notification.error({
551 message: 'Failed to save changes!',
554 if (onError) onError();
559 showUnsavedChangesAlert = (): Promise<any> => {
560 let modalTitle: string;
561 if (this.isAttributesTabSelected) {
562 modalTitle = `Unsaved attributes for ${this.selectedInstanceData.name}`;
563 } else if (this.isOutputsTabSelected) {
564 modalTitle = `Unsaved outputs for ${this.component.name}`;
567 return new Promise<any>((resolve, reject) => {
568 this.ModalServiceSdcUI.openCustomModal(
572 type: SdcUiCommon.ModalType.custom,
573 testId: "navigate-modal",
579 type: SdcUiCommon.ButtonType.secondary,
582 callback: () => reject()
587 type: SdcUiCommon.ButtonType.secondary,
591 this.reverseChangedData();
598 type: SdcUiCommon.ButtonType.primary,
601 disabled: !this.isValidChangedData,
602 callback: () => this.doSaveChangedData(resolve, reject)
604 ] as SdcUiCommon.IModalButtonComponent[]
605 } as SdcUiCommon.IModalConfig, UnsavedChangesComponent, {isValidChangedData: this.isValidChangedData});
610 updateAttributeValueAfterDeclare = (output: OutputFEModel) => {
611 const attributeList = this.instanceFeAttributesMap[output.instanceUniqueId];
613 const instanceName = output.instanceUniqueId.slice(output.instanceUniqueId.lastIndexOf('.') + 1);
614 const attributeForUpdatingVal = attributeList.find((feAttribute: AttributeFEModel) => {
615 return feAttribute.name == output.relatedAttributeName &&
616 (feAttribute.name == output.relatedAttributeName || output.name === instanceName.concat('_').concat(feAttribute.name.replace(/[.]/g, '_')));
618 const outputPath = (output.outputPath && output.outputPath != attributeForUpdatingVal.name) ? output.outputPath : undefined;
619 attributeForUpdatingVal.setAsDeclared(outputPath); //set attribute as declared before assigning value
620 // this.attributesService.disableRelatedAttributes(attributeForUpdatingVal, outputPath);
621 this.attributesUtils.resetAttributeValue(attributeForUpdatingVal, output.relatedAttributeValue, outputPath);
625 //used for declare button, to keep count of newly checked attributes (and ignore declared attributes)
626 updateCheckedAttributeCount = (increment: boolean): void => {
627 this.checkedAttributesCount += (increment) ? 1 : -1;
630 setOutputTabIndication = (numOutputs: number): void => {
631 this.attributeOutputTabs.setTabIndication('Outputs', numOutputs);
635 resetUnsavedChangesForOutput = (output: OutputFEModel) => {
636 this.outputsUtils.resetOutputDefaultValue(output, output.defaultValue);
637 this.changedData = this.changedData.filter((changedItem) => changedItem.uniqueId !== output.uniqueId);
638 this.updateHasChangedData();
641 deleteOutput = (output: OutputFEModel) => {
642 //reset any unsaved changes to the output before deleting it
643 this.resetUnsavedChangesForOutput(output);
645 let outputToDelete = new OutputBEModel(output);
647 this.componentServiceNg2
648 .deleteOutput(this.component, outputToDelete)
649 .subscribe(response => {
650 this.outputs = this.outputs.filter(output => output.uniqueId !== response.uniqueId);
652 //Reload the whole instance for now - TODO: CHANGE THIS after the BE starts returning attributes within the response, use commented code below instead!
653 this.changeSelectedInstance(this.selectedInstanceData);
655 this.Notification.error({
656 message: 'Failed to delete Output:' + error,
662 deleteAttribute = (attribute: AttributeFEModel) => {
663 const attributeToDelete = new AttributeFEModel(attribute);
664 this.loadingAttributes = true;
665 const feMap = this.instanceFeAttributesMap;
666 this.topologyTemplateService
667 .deleteServiceAttribute(this.component.uniqueId, attributeToDelete)
668 .subscribe((response) => {
669 const attribs = feMap[this.component.uniqueId];
670 attribs.splice(attribs.findIndex(p => p.uniqueId === response), 1);
672 this.Notification.error({
673 message: 'Failed to delete Attribute:' + error,
677 this.loadingAttributes = false;
681 addAttribute = () => {
682 let modalTitle = 'Add Attribute';
683 let modal = this.ModalService.createCustomModal(new ModalModel(
688 new ButtonModel('Save', 'blue', () => {
689 modal.instance.dynamicContent.instance.isLoading = true;
690 const newAttribute: AttributeBEModel = modal.instance.dynamicContent.instance.attributeModel;
691 this.topologyTemplateService.createServiceAttribute(this.component.uniqueId, newAttribute)
692 .subscribe((response) => {
693 modal.instance.dynamicContent.instance.isLoading = false;
694 const newAttrib: AttributeFEModel = this.attributesUtils.convertAddAttributeBEToAttributeFE(response);
695 this.instanceFeAttributesMap[this.component.uniqueId].push(newAttrib);
696 modal.instance.close();
698 modal.instance.dynamicContent.instance.isLoading = false;
699 this.Notification.error({
700 message: 'Failed to add Attribute:' + error,
704 }, () => !modal.instance.dynamicContent.instance.checkFormValidForSubmit()),
705 new ButtonModel('Cancel', 'outline grey', () => {
706 modal.instance.close();
711 this.ModalService.addDynamicContentToModal(modal, AttributeCreatorComponent, {});
712 modal.instance.open();
715 private isOutput = (instanceType: string): boolean => {
716 return instanceType === ResourceType.VF || instanceType === ResourceType.PNF || instanceType === ResourceType.CVFC || instanceType === ResourceType.CR;