Provide UI page for interface assignment in service for VFC instances
[sdc.git] / catalog-ui / src / app / ng2 / pages / interface-definition / interface-definition.page.component.ts
1 /*
2 * ============LICENSE_START=======================================================
3 * SDC
4 * ================================================================================
5 *  Copyright (C) 2022 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
10 *
11 *        http://www.apache.org/licenses/LICENSE-2.0
12 *  Unless required by applicable law or agreed to in writing, software
13 *  distributed under the License is distributed on an "AS IS" BASIS,
14 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 *  See the License for the specific language governing permissions and
16 *  limitations under the License.
17 *
18 *  SPDX-License-Identifier: Apache-2.0
19 *  ============LICENSE_END=========================================================
20 */
21 import {Component, ComponentRef, Inject, Input} from '@angular/core';
22 import {Component as IComponent} from 'app/models/components/component';
23 import {WorkflowServiceNg2} from 'app/ng2/services/workflow.service';
24 import {HierarchyDisplayOptions} from "../../components/logic/hierarchy-navigtion/hierarchy-display-options";
25 import {ISdcConfig, SdcConfigToken} from "app/ng2/config/sdc-config.config";
26 import {TranslateService} from "app/ng2/shared/translator/translate.service";
27 import {IModalButtonComponent, SdcUiServices} from 'onap-ui-angular';
28 import {ModalComponent} from 'app/ng2/components/ui/modal/modal.component';
29
30 import {ModalService} from 'app/ng2/services/modal.service';
31 import {
32     ArtifactModel,
33     ButtonModel,
34     CapabilitiesGroup,
35     InputBEModel,
36     InterfaceModel,
37     ComponentInstance,
38     ModalModel,
39     OperationModel,
40     WORKFLOW_ASSOCIATION_OPTIONS
41 } from 'app/models';
42
43 import {ComponentServiceNg2} from 'app/ng2/services/component-services/component.service';
44 import {TopologyTemplateService} from "../../services/component-services/topology-template.service";
45 import {InterfaceOperationModel} from "../../../models/interfaceOperation";
46 import {InterfaceOperationHandlerComponent} from "../composition/interface-operatons/operation-creator/interface-operation-handler.component";
47 import {DropdownValue} from "../../components/ui/form-components/dropdown/ui-element-dropdown.component";
48 import {ToscaArtifactModel} from "../../../models/toscaArtifact";
49 import {ToscaArtifactService} from "../../services/tosca-artifact.service";
50 import {InterfaceOperationComponent} from "../interface-operation/interface-operation.page.component";
51 import {Observable} from "rxjs/Observable";
52 import {PluginsService} from 'app/ng2/services/plugins.service';
53 import { InstanceFeDetails } from 'app/models/instance-fe-details';
54
55 export class UIOperationModel extends OperationModel {
56     isCollapsed: boolean = true;
57     isEllipsis: boolean;
58     MAX_LENGTH = 75;
59
60     constructor(operation: OperationModel) {
61         super(operation);
62         if (!operation.description) {
63             this.description = '';
64         }
65
66         if (this.description.length > this.MAX_LENGTH) {
67             this.isEllipsis = true;
68         } else {
69             this.isEllipsis = false;
70         }
71     }
72
73     getDescriptionEllipsis(): string {
74         if (this.isCollapsed && this.description.length > this.MAX_LENGTH) {
75             return this.description.substr(0, this.MAX_LENGTH - 3) + '...';
76         }
77         return this.description;
78     }
79
80     toggleCollapsed(e) {
81         e.stopPropagation();
82         this.isCollapsed = !this.isCollapsed;
83     }
84 }
85
86 class ModalTranslation {
87     CREATE_TITLE: string;
88     EDIT_TITLE: string;
89     DELETE_TITLE: string;
90     CANCEL_BUTTON: string;
91     SAVE_BUTTON: string;
92     CREATE_BUTTON: string;
93     DELETE_BUTTON: string;
94     deleteText: Function;
95
96     constructor(private TranslateService: TranslateService) {
97         this.TranslateService.languageChangedObservable.subscribe(lang => {
98             this.CREATE_TITLE = this.TranslateService.translate("INTERFACE_CREATE_TITLE");
99             this.EDIT_TITLE = this.TranslateService.translate('INTERFACE_EDIT_TITLE');
100             this.DELETE_TITLE = this.TranslateService.translate("INTERFACE_DELETE_TITLE");
101             this.CANCEL_BUTTON = this.TranslateService.translate("INTERFACE_CANCEL_BUTTON");
102             this.SAVE_BUTTON = this.TranslateService.translate("INTERFACE_SAVE_BUTTON");
103             this.CREATE_BUTTON = this.TranslateService.translate("INTERFACE_CREATE_BUTTON");
104             this.DELETE_BUTTON = this.TranslateService.translate("INTERFACE_DELETE_BUTTON");
105             this.deleteText = (operationName) => this.TranslateService.translate("INTERFACE_DELETE_TEXT", {operationName});
106         });
107     }
108 }
109
110 export class UIInterfaceModel extends InterfaceModel {
111     isCollapsed: boolean = false;
112
113     constructor(interf?: any) {
114         super(interf);
115         if (this.operations) {
116             this.operations = this.operations.map((operation) => new UIOperationModel(operation));
117         }
118     }
119
120     toggleCollapse() {
121         this.isCollapsed = !this.isCollapsed;
122     }
123 }
124
125 @Component({
126     selector: 'interface-definition',
127     templateUrl: './interface-definition.page.component.html',
128     styleUrls: ['interface-definition.page.component.less'],
129     providers: [ModalService, TranslateService, InterfaceOperationComponent]
130 })
131 export class InterfaceDefinitionComponent {
132
133     modalInstance: ComponentRef<ModalComponent>;
134     interfaces: UIInterfaceModel[];
135     inputs: InputBEModel[];
136
137     instancesNavigationData = [];
138     instances: any = [];
139     loadingInstances: boolean = false;
140     selectedInstanceData: any = null;
141     hierarchyInstancesDisplayOptions: HierarchyDisplayOptions = new HierarchyDisplayOptions('uniqueId', 'name', 'archived', null, 'iconClass');
142     disableFlag : boolean = true;
143
144     deploymentArtifactsFilePath: Array<DropdownValue> = [];
145
146     toscaArtifactTypes: Array<DropdownValue> = [];
147     interfaceTypesTest: Array<DropdownValue> = [];
148     interfaceTypesMap: Map<string, string[]>;
149
150     isLoading: boolean;
151     interfaceTypes: { [interfaceType: string]: string[] };
152     modalTranslation: ModalTranslation;
153     workflows: any[];
154     capabilities: CapabilitiesGroup;
155
156     openOperation: OperationModel;
157     enableWorkflowAssociation: boolean;
158     workflowIsOnline: boolean;
159     validImplementationProps:boolean = true;
160     serviceInterfaces: InterfaceModel[];
161
162     @Input() component: IComponent;
163     @Input() readonly: boolean;
164     @Input() enableMenuItems: Function;
165     @Input() disableMenuItems: Function;
166
167     constructor(
168         @Inject(SdcConfigToken) private sdcConfig: ISdcConfig,
169         @Inject("$state") private $state: ng.ui.IStateService,
170         @Inject("Notification") private notification: any,
171         private translateService: TranslateService,
172         private componentServiceNg2: ComponentServiceNg2,
173         private modalServiceNg2: ModalService,
174         private modalServiceSdcUI: SdcUiServices.ModalService,
175         private topologyTemplateService: TopologyTemplateService,
176         private toscaArtifactService: ToscaArtifactService,
177         private ComponentServiceNg2: ComponentServiceNg2,
178         private WorkflowServiceNg2: WorkflowServiceNg2,
179         private ModalServiceSdcUI: SdcUiServices.ModalService,
180         private PluginsService: PluginsService
181     ) {
182         this.modalTranslation = new ModalTranslation(translateService);
183         this.interfaceTypesMap = new Map<string, string[]>();
184     }
185
186     ngOnInit(): void {
187         this.isLoading = true;
188         this.interfaces = [];
189         //this.disableFlag = this.readonly;
190         this.workflowIsOnline = !_.isUndefined(this.PluginsService.getPluginByStateUrl('workflowDesigner'));
191         Observable.forkJoin(
192             this.ComponentServiceNg2.getInterfaceOperations(this.component),
193             this.ComponentServiceNg2.getComponentInputs(this.component),
194             this.ComponentServiceNg2.getInterfaceTypes(this.component),
195             this.ComponentServiceNg2.getCapabilitiesAndRequirements(this.component.componentType, this.component.uniqueId),
196             this.componentServiceNg2.getComponentResourcePropertiesData(this.component)
197         ).subscribe((response: any[]) => {
198             const callback = (workflows) => {
199                 this.isLoading = false;
200                 this.serviceInterfaces = response[0].interfaces;
201                 this.initInterfaces(response[0].interfaces);
202                 this.sortInterfaces();
203                 this.inputs = response[1].inputs;
204                 this.interfaceTypes = response[2];
205                 this.workflows = (workflows.items) ? workflows.items : workflows;
206                 this.capabilities = response[3].capabilities;
207                 this.instances = response[4].componentInstances;
208                 const serviceInstance = new ComponentInstance();
209                 serviceInstance.name = "SELF";
210                 serviceInstance.uniqueId = this.component.uniqueId;
211                 if (this.instances != null) {
212                     this.instances.unshift(serviceInstance);
213                 } else {
214                     this.instances = [serviceInstance];
215                 }
216                 _.forEach(this.instances, (instance) => {
217                     this.instancesNavigationData.push(instance);
218                 });
219                 this.onInstanceSelectedUpdate(this.instancesNavigationData[0]);
220                 this.loadingInstances = false;
221                 
222             };
223             if (this.enableWorkflowAssociation && this.workflowIsOnline) {
224                 this.WorkflowServiceNg2.getWorkflows().subscribe(
225                     callback,
226                     (err) => {
227                         this.workflowIsOnline = false;
228                         callback([]);
229                     }
230                 );
231             } else {
232                 callback([]);
233             }
234         });
235
236         this.loadToscaArtifacts();
237     }
238
239     onInstanceSelectedUpdate = (instance: any) => {
240         this.selectedInstanceData = instance;
241         if (instance.name != "SELF") {
242             this.disableFlag = true;
243             let newInterfaces : InterfaceModel[] = [];
244             if (instance.interfaces instanceof Array) {
245                 instance.interfaces.forEach(result => {
246                     let interfaceObj = new InterfaceModel();
247                     interfaceObj.type = result.type;
248                     interfaceObj.uniqueId = result.uniqueId;
249                     if (result.operations instanceof Array) {
250                         interfaceObj.operations = result.operations;
251                     } else if (!_.isEmpty(result.operations)) {
252                         interfaceObj.operations = [];
253                         Object.keys(result.operations).forEach(name => {
254                             interfaceObj.operations.push(result.operations[name]);
255                         });
256                     }
257                     newInterfaces.push(interfaceObj);
258                 });
259             } else {
260                 Object.keys(instance.interfaces).forEach(key => {
261                     let obj = instance.interfaces[key];
262                     let interfaceObj = new InterfaceModel();
263                     interfaceObj.type = obj.type;
264                     interfaceObj.uniqueId = obj.uniqueId;
265                     if (obj.operations instanceof Array) {
266                         interfaceObj.operations = obj.operations;
267                     } else if (!_.isEmpty(obj.operations)) {
268                         interfaceObj.operations = [];
269                         Object.keys(obj.operations).forEach(name => {
270                             interfaceObj.operations.push(obj.operations[name]);
271                         });
272                     }
273                     newInterfaces.push(interfaceObj);
274                 });
275             }
276             this.interfaces = newInterfaces.map((interf) => new UIInterfaceModel(interf));
277         } else {
278             //this.disableFlag = this.readonly;
279             this.interfaces = this.serviceInterfaces.map((interf) => new UIInterfaceModel(interf));
280         }
281         this.sortInterfaces();
282     }
283
284     initInterfaces(interfaces: InterfaceModel[]): void {
285         if (interfaces) {
286             this.interfaces = interfaces.map((interf) => new UIInterfaceModel(interf));
287         }
288     }
289
290     private cancelAndCloseModal = () => {
291         return this.modalServiceNg2.closeCurrentModal();
292     }
293
294     private disableSaveButton = (): boolean => {
295         let disable:boolean = true;
296         if(this.readonly) {
297             return disable;
298         }
299         if (this.component.isService()) {
300             return disable;
301         }
302     
303         let selectedInterfaceOperation = this.modalInstance.instance.dynamicContent.instance.selectedInterfaceOperation;
304         let isInterfaceOperation:boolean = !(typeof selectedInterfaceOperation == 'undefined' || _.isEmpty(selectedInterfaceOperation));
305         let selectedInterfaceType = this.modalInstance.instance.dynamicContent.instance.selectedInterfaceType;
306         let isInterfaceType:boolean = !(typeof selectedInterfaceType == 'undefined' || _.isEmpty(selectedInterfaceType));
307         let bothSet: boolean = isInterfaceOperation && isInterfaceType;
308     
309         let enableAddArtifactImplementation = this.modalInstance.instance.dynamicContent.instance.enableAddArtifactImplementation;
310         if(enableAddArtifactImplementation) {
311             let validImplementationProps = this.modalInstance.instance.dynamicContent.instance.validImplementationProps;
312             let toscaArtifactTypeSelected = this.modalInstance.instance.dynamicContent.instance.toscaArtifactTypeSelected;
313             let isToscaArtifactType:boolean = !(typeof toscaArtifactTypeSelected == 'undefined' || _.isEmpty(toscaArtifactTypeSelected));
314             disable = !bothSet || !isToscaArtifactType || !validImplementationProps;
315             return disable;
316         }
317         disable = !bothSet;
318         return disable;
319     }
320
321     onSelectInterfaceOperation(interfaceModel: UIInterfaceModel, operation: InterfaceOperationModel) {
322         const isEdit = operation !== undefined;
323         const modalButtons = [];
324         if (!this.readonly) {
325             const saveButton: ButtonModel = new ButtonModel(this.modalTranslation.SAVE_BUTTON, 'blue',
326                 () => isEdit ? this.updateOperation() : this.createOperationCallback(),
327                 this.disableSaveButton
328             );
329             modalButtons.push(saveButton);
330         }
331         modalButtons.push(new ButtonModel(this.modalTranslation.CANCEL_BUTTON, 'outline white', this.cancelAndCloseModal));
332         const interfaceDataModal: ModalModel =
333             new ModalModel('l', this.modalTranslation.EDIT_TITLE, '', modalButtons, 'custom');
334         this.modalInstance = this.modalServiceNg2.createCustomModal(interfaceDataModal);
335
336         this.modalServiceNg2.addDynamicContentToModal(
337             this.modalInstance,
338             InterfaceOperationHandlerComponent,
339             {
340                 deploymentArtifactsFilePath: this.deploymentArtifactsFilePath,
341                 toscaArtifactTypes: this.toscaArtifactTypes,
342                 selectedInterface: interfaceModel ? interfaceModel : new UIInterfaceModel(),
343                 selectedInterfaceOperation: operation ? operation : new InterfaceOperationModel(),
344                 validityChangedCallback: this.disableSaveButton,
345                 isViewOnly: this.readonly,
346                 validImplementationProps: this.validImplementationProps,
347                 'isEdit': isEdit,
348                 interfaceTypesMap: this.interfaceTypesMap,
349                 modelName: this.component.model
350             }
351         );
352         this.modalInstance.instance.open();
353     }
354
355     private updateOperation = (): void => {
356         this.modalServiceNg2.currentModal.instance.dynamicContent.instance.isLoading = true;
357         const interfaceOperationHandlerComponentInstance: InterfaceOperationHandlerComponent = this.modalInstance.instance.dynamicContent.instance;
358         const operationToUpdate = this.modalInstance.instance.dynamicContent.instance.operationToUpdate;
359         let timeout = null;
360         if (operationToUpdate.implementation && operationToUpdate.implementation.timeout != null) {
361             timeout = operationToUpdate.implementation.timeout;
362         }
363         const isArtifactChecked = interfaceOperationHandlerComponentInstance.enableAddArtifactImplementation;
364         if (!isArtifactChecked) {
365             const artifactName = interfaceOperationHandlerComponentInstance.artifactName ?
366                 interfaceOperationHandlerComponentInstance.artifactName : '';
367             operationToUpdate.implementation = new ArtifactModel({'artifactName': artifactName, 'artifactVersion': ''} as ArtifactModel);
368         }
369         if (timeout != null) {
370             operationToUpdate.implementation.timeout = timeout;
371         }
372         this.componentServiceNg2.updateComponentInterfaceOperation(this.component.uniqueId, operationToUpdate)
373         .subscribe((newOperation: InterfaceOperationModel) => {
374             let oldOpIndex;
375             let oldInterf;
376             this.interfaces.forEach(interf => {
377                 interf.operations.forEach(op => {
378                     if (op.uniqueId === newOperation.uniqueId) {
379                         oldInterf = interf;
380                         oldOpIndex = interf.operations.findIndex((el) => el.uniqueId === op.uniqueId);
381                     }
382                 });
383             });
384             oldInterf.operations.splice(oldOpIndex, 1);
385             oldInterf.operations.push(new InterfaceOperationModel(newOperation));
386         }, error => {
387             this.modalServiceNg2.currentModal.instance.dynamicContent.instance.isLoading = false;
388         }, () => {
389             this.sortInterfaces();
390             this.modalServiceNg2.currentModal.instance.dynamicContent.instance.isLoading = false;
391             this.modalServiceNg2.closeCurrentModal();
392         });
393     }
394
395     private createOperationCallback(): void {
396         this.modalServiceNg2.currentModal.instance.dynamicContent.instance.isLoading = true;
397         const operationToUpdate = this.modalInstance.instance.dynamicContent.instance.operationToUpdate;
398         console.log('createOperationCallback', operationToUpdate);
399         console.log('this.component', this.component);
400         this.componentServiceNg2.createComponentInterfaceOperation(this.component.uniqueId, this.component.getTypeUrl(), operationToUpdate)
401         .subscribe((newOperation: InterfaceOperationModel) => {
402             const foundInterface = this.interfaces.find(value => value.type === newOperation.interfaceType);
403             if (foundInterface) {
404                 foundInterface.operations.push(new UIOperationModel(new OperationModel(newOperation)));
405             } else {
406                 const uiInterfaceModel = new UIInterfaceModel();
407                 uiInterfaceModel.type = newOperation.interfaceType;
408                 uiInterfaceModel.uniqueId = newOperation.interfaceType;
409                 uiInterfaceModel.operations = [];
410                 uiInterfaceModel.operations.push(new UIOperationModel(new OperationModel(newOperation)));
411                 this.interfaces.push(uiInterfaceModel);
412             }
413         }, error => {
414             this.modalServiceNg2.currentModal.instance.dynamicContent.instance.isLoading = false;
415         }, () => {
416             this.modalServiceNg2.currentModal.instance.dynamicContent.instance.isLoading = false;
417             this.modalServiceNg2.closeCurrentModal();
418         });
419     }
420
421     private handleEnableAddArtifactImplementation = (newOperation: InterfaceOperationModel): InterfaceOperationModel => {
422         if (!this.isEnableAddArtifactImplementation()) {
423             newOperation.implementation.artifactType = null;
424             newOperation.implementation.artifactVersion = null;
425         }
426         return newOperation;
427     }
428
429     private isEnableAddArtifactImplementation = (): boolean => {
430         return this.modalInstance.instance.dynamicContent.enableAddArtifactImplementation;
431     }
432
433     private initInterfaceDefinition() {
434         this.isLoading = true;
435         this.interfaces = [];
436         this.topologyTemplateService.getComponentInterfaceOperations(this.component.componentType, this.component.uniqueId)
437         .subscribe((response) => {
438             if (response.interfaces) {
439                 this.interfaces = response.interfaces.map((interfaceModel) => new UIInterfaceModel(interfaceModel));
440             }
441             this.isLoading = false;
442         });
443     }
444
445     private loadToscaArtifacts() {
446         this.toscaArtifactService.getToscaArtifacts(this.component.model).subscribe(response => {
447             if (response) {
448                 let toscaArtifactsFound = <ToscaArtifactModel[]>_.values(response);
449                 toscaArtifactsFound.forEach(value => this.toscaArtifactTypes.push(new DropdownValue(value, value.type)));
450             }
451         }, error => {
452             this.notification.error({
453                 message: 'Failed to Load Tosca Artifacts:' + error,
454                 title: 'Failure'
455             });
456         });
457     }
458
459     private loadInterfaceTypes() {
460         this.componentServiceNg2.getInterfaceTypes(this.component).subscribe(response => {
461             if (response) {
462                 console.info("loadInterfaceTypes ", response);
463                 for (const interfaceType in response) {
464                     this.interfaceTypesMap.set(interfaceType, response[interfaceType]);
465                     this.interfaceTypesTest.push(new DropdownValue(interfaceType, interfaceType));
466                 }
467             }
468         }, error => {
469             this.notification.error({
470                 message: 'Failed to Load Interface Types:' + error,
471                 title: 'Failure'
472             });
473         });
474     }
475
476     collapseAll(value: boolean = true): void {
477         this.interfaces.forEach(interfaceData => {
478             interfaceData.isCollapsed = value;
479         });
480     }
481
482     isAllCollapsed(): boolean {
483         return this.interfaces.every((interfaceData) => interfaceData.isCollapsed);
484     }
485
486     isAllExpanded(): boolean {
487         return this.interfaces.every((interfaceData) => !interfaceData.isCollapsed);
488     }
489
490     isInterfaceListEmpty(): boolean {
491         return this.interfaces.length === 0;
492     }
493
494     isOperationListEmpty(): boolean {
495         return this.interfaces.filter((interfaceData) => interfaceData.operations && interfaceData.operations.length > 0).length > 0;
496     }
497
498     onRemoveOperation(operation: OperationModel): void {
499         if (this.readonly) {
500             return;
501         }
502
503         const deleteButton: IModalButtonComponent = {
504             id: 'deleteButton',
505             text: this.modalTranslation.DELETE_BUTTON,
506             type: 'primary',
507             size: 'small',
508             closeModal: true,
509             callback: () => {
510                 this.ComponentServiceNg2
511                 .deleteInterfaceOperation(this.component, operation)
512                 .subscribe(() => {
513                     const curInterf = this.interfaces.find((interf) => interf.type === operation.interfaceType);
514                     const index = curInterf.operations.findIndex((el) => el.uniqueId === operation.uniqueId);
515                     curInterf.operations.splice(index, 1);
516                     if (!curInterf.operations.length) {
517                         const interfIndex = this.interfaces.findIndex((interf) => interf.type === operation.interfaceType);
518                         this.interfaces.splice(interfIndex, 1);
519                     }
520                 });
521             }
522         };
523
524         const cancelButton: IModalButtonComponent = {
525             id: 'cancelButton',
526             text: this.modalTranslation.CANCEL_BUTTON,
527             type: 'secondary',
528             size: 'small',
529             closeModal: true,
530             callback: () => {
531                 this.openOperation = null;
532             },
533         };
534
535         this.ModalServiceSdcUI.openWarningModal(
536             this.modalTranslation.DELETE_TITLE,
537             this.modalTranslation.deleteText(operation.name),
538             'deleteOperationModal',
539             [deleteButton, cancelButton],
540         );
541     }
542
543     private createOperation = (operation: OperationModel): void => {
544         this.ComponentServiceNg2.createInterfaceOperation(this.component, operation).subscribe((response: OperationModel) => {
545             this.openOperation = null;
546
547             let curInterf = this.interfaces.find((interf) => interf.type === operation.interfaceType);
548
549             if (!curInterf) {
550                 curInterf = new UIInterfaceModel({
551                     type: response.interfaceType,
552                     uniqueId: response.uniqueId,
553                     operations: []
554                 });
555                 this.interfaces.push(curInterf);
556             }
557
558             const newOpModel = new UIOperationModel(response);
559             curInterf.operations.push(newOpModel);
560             this.sortInterfaces();
561
562             if (operation.workflowAssociationType === WORKFLOW_ASSOCIATION_OPTIONS.EXTERNAL && operation.artifactData) {
563                 this.ComponentServiceNg2.uploadInterfaceOperationArtifact(this.component, newOpModel, operation).subscribe();
564             } else if (response.workflowId && operation.workflowAssociationType === WORKFLOW_ASSOCIATION_OPTIONS.EXISTING) {
565                 this.WorkflowServiceNg2.associateWorkflowArtifact(this.component, response).subscribe();
566             } else if (operation.workflowAssociationType === WORKFLOW_ASSOCIATION_OPTIONS.NEW) {
567                 this.$state.go('workspace.plugins', {path: 'workflowDesigner'});
568             }
569         });
570     }
571
572     private enableOrDisableSaveButton = (shouldEnable: boolean): void => {
573         const saveButton = this.modalInstance.instance.dynamicContent.getButtonById('saveButton');
574         saveButton.disabled = !shouldEnable;
575     }
576
577     private sortInterfaces(): void {
578         this.interfaces = this.interfaces.filter((interf) => interf.operations && interf.operations.length > 0); // remove empty interfaces
579         this.interfaces.sort((a, b) => a.type.localeCompare(b.type)); // sort interfaces alphabetically
580         this.interfaces.forEach((interf) => {
581             interf.operations.sort((a, b) => a.name.localeCompare(b.name)); // sort operations alphabetically
582         });
583     }
584
585 }