2 * ============LICENSE_START=======================================================
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
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.
18 * SPDX-License-Identifier: Apache-2.0
19 * ============LICENSE_END=========================================================
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 import {ResourceType, ComponentType} from "app/utils";
30 import {ModalService} from 'app/ng2/services/modal.service';
40 WORKFLOW_ASSOCIATION_OPTIONS
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';
55 export class UIOperationModel extends OperationModel {
56 isCollapsed: boolean = true;
60 constructor(operation: OperationModel) {
62 if (!operation.description) {
63 this.description = '';
66 if (this.description.length > this.MAX_LENGTH) {
67 this.isEllipsis = true;
69 this.isEllipsis = false;
73 getDescriptionEllipsis(): string {
74 if (this.isCollapsed && this.description.length > this.MAX_LENGTH) {
75 return this.description.substr(0, this.MAX_LENGTH - 3) + '...';
77 return this.description;
82 this.isCollapsed = !this.isCollapsed;
86 class ModalTranslation {
90 CANCEL_BUTTON: string;
92 CREATE_BUTTON: string;
93 DELETE_BUTTON: string;
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});
110 export class UIInterfaceModel extends InterfaceModel {
111 isCollapsed: boolean = false;
113 constructor(interf?: any) {
115 if (this.operations) {
116 this.operations = this.operations.map((operation) => new UIOperationModel(operation));
121 this.isCollapsed = !this.isCollapsed;
126 selector: 'interface-definition',
127 templateUrl: './interface-definition.page.component.html',
128 styleUrls: ['interface-definition.page.component.less'],
129 providers: [ModalService, TranslateService, InterfaceOperationComponent]
131 export class InterfaceDefinitionComponent {
133 modalInstance: ComponentRef<ModalComponent>;
134 interfaces: UIInterfaceModel[];
135 inputs: InputBEModel[];
137 instancesNavigationData = [];
139 loadingInstances: boolean = false;
140 selectedInstanceData: any = null;
141 hierarchyInstancesDisplayOptions: HierarchyDisplayOptions = new HierarchyDisplayOptions('uniqueId', 'name', 'archived', null, 'iconClass');
142 disableFlag : boolean = true;
144 deploymentArtifactsFilePath: Array<DropdownValue> = [];
146 toscaArtifactTypes: Array<DropdownValue> = [];
147 interfaceTypesTest: Array<DropdownValue> = [];
148 interfaceTypesMap: Map<string, string[]>;
151 interfaceTypes: { [interfaceType: string]: string[] };
152 modalTranslation: ModalTranslation;
154 capabilities: CapabilitiesGroup;
156 openOperation: OperationModel;
157 enableWorkflowAssociation: boolean;
158 workflowIsOnline: boolean;
159 validImplementationProps: boolean = true;
160 validMilestoneActivities: boolean = true;
161 validMilestoneFilters: boolean = true;
162 serviceInterfaces: InterfaceModel[];
164 @Input() component: IComponent;
165 @Input() readonly: boolean;
166 @Input() enableMenuItems: Function;
167 @Input() disableMenuItems: Function;
170 @Inject(SdcConfigToken) private sdcConfig: ISdcConfig,
171 @Inject("$state") private $state: ng.ui.IStateService,
172 @Inject("Notification") private notification: any,
173 private translateService: TranslateService,
174 private componentServiceNg2: ComponentServiceNg2,
175 private modalServiceNg2: ModalService,
176 private modalServiceSdcUI: SdcUiServices.ModalService,
177 private topologyTemplateService: TopologyTemplateService,
178 private toscaArtifactService: ToscaArtifactService,
179 private ComponentServiceNg2: ComponentServiceNg2,
180 private WorkflowServiceNg2: WorkflowServiceNg2,
181 private ModalServiceSdcUI: SdcUiServices.ModalService,
182 private PluginsService: PluginsService
184 this.modalTranslation = new ModalTranslation(translateService);
185 this.interfaceTypesMap = new Map<string, string[]>();
189 this.isLoading = true;
190 this.interfaces = [];
191 this.workflowIsOnline = !_.isUndefined(this.PluginsService.getPluginByStateUrl('workflowDesigner'));
193 this.ComponentServiceNg2.getInterfaceOperations(this.component),
194 this.ComponentServiceNg2.getComponentInputs(this.component),
195 this.ComponentServiceNg2.getInterfaceTypes(this.component),
196 this.ComponentServiceNg2.getCapabilitiesAndRequirements(this.component.componentType, this.component.uniqueId),
197 this.componentServiceNg2.getComponentResourcePropertiesData(this.component)
198 ).subscribe((response: any[]) => {
199 const callback = (workflows) => {
200 this.isLoading = false;
201 this.serviceInterfaces = response[0].interfaces;
202 this.initInterfaces(response[0].interfaces);
203 this.sortInterfaces();
204 this.inputs = response[1].inputs;
205 this.interfaceTypes = response[2];
206 this.workflows = (workflows.items) ? workflows.items : workflows;
207 this.capabilities = response[3].capabilities;
208 this.instances = response[4].componentInstances;
209 const serviceInstance = new ComponentInstance();
210 serviceInstance.name = "SELF";
211 serviceInstance.uniqueId = this.component.uniqueId;
212 if (this.instances != null) {
213 this.instances.unshift(serviceInstance);
215 this.instances = [serviceInstance];
217 _.forEach(this.instances, (instance) => {
218 this.instancesNavigationData.push(instance);
220 this.onInstanceSelectedUpdate(this.instancesNavigationData[0]);
221 this.loadingInstances = false;
224 if (this.enableWorkflowAssociation && this.workflowIsOnline) {
225 this.WorkflowServiceNg2.getWorkflows().subscribe(
228 this.workflowIsOnline = false;
237 this.loadToscaArtifacts();
240 onInstanceSelectedUpdate = (instance: any) => {
241 this.interfaces = [];
242 this.selectedInstanceData = instance;
243 if (instance.name != "SELF") {
244 this.disableFlag = !this.isAllowAddOperation(instance.originType);
245 if (!instance.interfaces) {
248 let newInterfaces : InterfaceModel[] = [];
249 if (instance.interfaces instanceof Array) {
250 instance.interfaces.forEach(result => {
251 let interfaceObj = new InterfaceModel();
252 interfaceObj.type = result.type;
253 interfaceObj.uniqueId = result.uniqueId;
254 if (result.operations instanceof Array) {
255 interfaceObj.operations = result.operations;
256 } else if (!_.isEmpty(result.operations)) {
257 interfaceObj.operations = [];
258 Object.keys(result.operations).forEach(name => {
259 result.operations[name].interfaceId = result.type;
260 result.operations[name].interfaceType = result.type;
261 interfaceObj.operations.push(result.operations[name]);
264 newInterfaces.push(interfaceObj);
267 Object.keys(instance.interfaces).forEach(key => {
268 let obj = instance.interfaces[key];
269 let interfaceObj = new InterfaceModel();
270 interfaceObj.type = obj.type;
271 interfaceObj.uniqueId = obj.uniqueId;
272 if (obj.operations instanceof Array) {
273 interfaceObj.operations = obj.operations;
274 } else if (!_.isEmpty(obj.operations)) {
275 interfaceObj.operations = [];
276 Object.keys(obj.operations).forEach(name => {
277 obj.operations[name].interfaceId = key;
278 obj.operations[name].interfaceType = key;
279 interfaceObj.operations.push(obj.operations[name]);
282 newInterfaces.push(interfaceObj);
285 this.interfaces = newInterfaces.map((interf) => new UIInterfaceModel(interf));
287 this.disableFlag = true;
288 this.interfaces = this.serviceInterfaces.map((interf) => new UIInterfaceModel(interf));
290 this.sortInterfaces();
293 isAllowAddOperation(originType: string): boolean {
294 if (originType && (originType === ResourceType.VFC || originType === ResourceType.CP || originType === ResourceType.VL)){
300 initInterfaces(interfaces: InterfaceModel[]): void {
302 this.interfaces = interfaces.map((interf) => new UIInterfaceModel(interf));
306 private cancelAndCloseModal = () => {
307 return this.modalServiceNg2.closeCurrentModal();
310 private disableSaveButton = (): boolean => {
311 let disable:boolean = true;
315 const validMilestoneActivities = this.modalInstance.instance.dynamicContent.instance.validMilestoneActivities;
316 const validMilestoneFilters = this.modalInstance.instance.dynamicContent.instance.validMilestoneFilters;
317 if (!validMilestoneActivities || !validMilestoneFilters) {
321 let selectedInterfaceOperation = this.modalInstance.instance.dynamicContent.instance.selectedInterfaceOperation;
322 let isInterfaceOperation:boolean = !(typeof selectedInterfaceOperation == 'undefined' || _.isEmpty(selectedInterfaceOperation));
323 let selectedInterfaceType = this.modalInstance.instance.dynamicContent.instance.selectedInterfaceType;
324 let isInterfaceType:boolean = !(typeof selectedInterfaceType == 'undefined' || _.isEmpty(selectedInterfaceType));
325 let bothSet: boolean = isInterfaceOperation && isInterfaceType;
327 let enableAddArtifactImplementation = this.modalInstance.instance.dynamicContent.instance.enableAddArtifactImplementation;
328 if(enableAddArtifactImplementation) {
329 let validImplementationProps = this.modalInstance.instance.dynamicContent.instance.validImplementationProps;
330 let toscaArtifactTypeSelected = this.modalInstance.instance.dynamicContent.instance.toscaArtifactTypeSelected;
331 let isToscaArtifactType:boolean = !(typeof toscaArtifactTypeSelected == 'undefined' || _.isEmpty(toscaArtifactTypeSelected));
332 disable = !bothSet || !isToscaArtifactType || !validImplementationProps;
339 onSelectInterfaceOperation(interfaceModel: UIInterfaceModel, operation: InterfaceOperationModel) {
340 const isEdit = operation !== undefined;
341 const modalButtons = [];
342 if (!this.readonly) {
343 const saveButton: ButtonModel = new ButtonModel(this.modalTranslation.SAVE_BUTTON, 'blue',
344 () => isEdit ? this.updateOperation() : this.createOperationCallback(),
345 this.disableSaveButton
347 modalButtons.push(saveButton);
349 modalButtons.push(new ButtonModel(this.modalTranslation.CANCEL_BUTTON, 'outline white', this.cancelAndCloseModal));
350 const interfaceDataModal: ModalModel =
351 new ModalModel('l', this.modalTranslation.EDIT_TITLE, '', modalButtons, 'custom');
352 this.modalInstance = this.modalServiceNg2.createCustomModal(interfaceDataModal);
354 this.modalServiceNg2.addDynamicContentToModal(
356 InterfaceOperationHandlerComponent,
358 deploymentArtifactsFilePath: this.deploymentArtifactsFilePath,
359 toscaArtifactTypes: this.toscaArtifactTypes,
360 selectedInterface: interfaceModel ? interfaceModel : new UIInterfaceModel(),
361 selectedInterfaceOperation: operation ? operation : new InterfaceOperationModel(),
362 validityChangedCallback: this.disableSaveButton,
363 isViewOnly: this.readonly,
364 validImplementationProps: this.validImplementationProps,
365 validMilestoneActivities: this.validMilestoneActivities,
366 validMilestoneFilters: this.validMilestoneFilters,
368 interfaceTypesMap: this.interfaceTypesMap,
369 modelName: this.component.model
372 this.modalInstance.instance.open();
375 private updateOperation = (): void => {
376 this.modalServiceNg2.currentModal.instance.dynamicContent.instance.isLoading = true;
377 const interfaceOperationHandlerComponentInstance: InterfaceOperationHandlerComponent = this.modalInstance.instance.dynamicContent.instance;
378 const operationToUpdate = this.modalInstance.instance.dynamicContent.instance.operationToUpdate;
380 if (operationToUpdate.implementation && operationToUpdate.implementation.timeout != null) {
381 timeout = operationToUpdate.implementation.timeout;
383 const isArtifactChecked = interfaceOperationHandlerComponentInstance.enableAddArtifactImplementation;
384 if (!isArtifactChecked) {
385 const artifactName = interfaceOperationHandlerComponentInstance.artifactName ?
386 interfaceOperationHandlerComponentInstance.artifactName : '';
387 operationToUpdate.implementation = new ArtifactModel({'artifactName': artifactName, 'artifactVersion': ''} as ArtifactModel);
389 if (timeout != null) {
390 operationToUpdate.implementation.timeout = timeout;
392 if (this.component.componentType === ComponentType.SERVICE) {
393 this.topologyTemplateService.updateComponentInstanceInterfaceOperation(this.component.uniqueId, this.component.componentType, this.selectedInstanceData.uniqueId, operationToUpdate)
394 .subscribe((res) => {
395 const interf: InterfaceModel = _.find(res.interfaces, interf => interf.type === operationToUpdate.interfaceType);
396 const newOp: OperationModel = _.find(interf.operations, op => op.name === operationToUpdate.name);
397 let newOperation = new InterfaceOperationModel({
399 interfaceType: interf.type,
400 interfaceId: interf.uniqueId,
404 this.interfaces.forEach(interf => {
405 interf.operations.forEach(op => {
406 if (op.uniqueId === newOperation.uniqueId) {
408 oldOpIndex = interf.operations.findIndex((el) => el.uniqueId === op.uniqueId);
412 oldInterf.operations.splice(oldOpIndex, 1);
413 oldInterf.operations.push(new InterfaceOperationModel(newOperation));
415 this.modalServiceNg2.currentModal.instance.dynamicContent.instance.isLoading = false;
417 this.sortInterfaces();
418 this.modalServiceNg2.currentModal.instance.dynamicContent.instance.isLoading = false;
419 this.modalServiceNg2.closeCurrentModal();
422 this.componentServiceNg2.updateComponentInterfaceOperation(this.component.uniqueId, operationToUpdate)
423 .subscribe((newOperation: InterfaceOperationModel) => {
426 this.interfaces.forEach(interf => {
427 interf.operations.forEach(op => {
428 if (op.uniqueId === newOperation.uniqueId) {
430 oldOpIndex = interf.operations.findIndex((el) => el.uniqueId === op.uniqueId);
434 oldInterf.operations.splice(oldOpIndex, 1);
435 oldInterf.operations.push(new InterfaceOperationModel(newOperation));
437 this.modalServiceNg2.currentModal.instance.dynamicContent.instance.isLoading = false;
439 this.sortInterfaces();
440 this.modalServiceNg2.currentModal.instance.dynamicContent.instance.isLoading = false;
441 this.modalServiceNg2.closeCurrentModal();
446 private createOperationCallback(): void {
447 this.modalServiceNg2.currentModal.instance.dynamicContent.instance.isLoading = true;
448 const operationToUpdate = this.modalInstance.instance.dynamicContent.instance.operationToUpdate;
449 if (this.component.componentType === ComponentType.SERVICE) {
450 this.topologyTemplateService.createComponentInstanceInterfaceOperation(this.component.uniqueId, this.selectedInstanceData.uniqueId, operationToUpdate)
451 .subscribe((newOperation: InterfaceOperationModel) => {
452 const foundInterface = this.interfaces.find(value => value.type === newOperation.interfaceType);
453 if (foundInterface) {
454 foundInterface.operations.push(new UIOperationModel(new OperationModel(newOperation)));
456 const uiInterfaceModel = new UIInterfaceModel();
457 uiInterfaceModel.type = newOperation.interfaceType;
458 uiInterfaceModel.uniqueId = newOperation.interfaceType;
459 uiInterfaceModel.operations = [];
460 uiInterfaceModel.operations.push(new UIOperationModel(new OperationModel(newOperation)));
461 this.interfaces.push(uiInterfaceModel);
464 this.modalServiceNg2.currentModal.instance.dynamicContent.instance.isLoading = false;
465 this.modalServiceNg2.closeCurrentModal();
467 this.modalServiceNg2.currentModal.instance.dynamicContent.instance.isLoading = false;
468 this.modalServiceNg2.closeCurrentModal();
471 this.componentServiceNg2.createComponentInterfaceOperation(this.component.uniqueId, this.component.getTypeUrl(), operationToUpdate)
472 .subscribe((newOperation: InterfaceOperationModel) => {
473 const foundInterface = this.interfaces.find(value => value.type === newOperation.interfaceType);
474 if (foundInterface) {
475 foundInterface.operations.push(new UIOperationModel(new OperationModel(newOperation)));
477 const uiInterfaceModel = new UIInterfaceModel();
478 uiInterfaceModel.type = newOperation.interfaceType;
479 uiInterfaceModel.uniqueId = newOperation.interfaceType;
480 uiInterfaceModel.operations = [];
481 uiInterfaceModel.operations.push(new UIOperationModel(new OperationModel(newOperation)));
482 this.interfaces.push(uiInterfaceModel);
485 this.modalServiceNg2.currentModal.instance.dynamicContent.instance.isLoading = false;
486 this.modalServiceNg2.closeCurrentModal();
488 this.modalServiceNg2.currentModal.instance.dynamicContent.instance.isLoading = false;
489 this.modalServiceNg2.closeCurrentModal();
494 private handleEnableAddArtifactImplementation = (newOperation: InterfaceOperationModel): InterfaceOperationModel => {
495 if (!this.isEnableAddArtifactImplementation()) {
496 newOperation.implementation.artifactType = null;
497 newOperation.implementation.artifactVersion = null;
502 private isEnableAddArtifactImplementation = (): boolean => {
503 return this.modalInstance.instance.dynamicContent.enableAddArtifactImplementation;
506 private initInterfaceDefinition() {
507 this.isLoading = true;
508 this.interfaces = [];
509 this.topologyTemplateService.getComponentInterfaceOperations(this.component.componentType, this.component.uniqueId)
510 .subscribe((response) => {
511 if (response.interfaces) {
512 this.interfaces = response.interfaces.map((interfaceModel) => new UIInterfaceModel(interfaceModel));
514 this.isLoading = false;
518 private loadToscaArtifacts() {
519 this.toscaArtifactService.getToscaArtifacts(this.component.model).subscribe(response => {
521 let toscaArtifactsFound = <ToscaArtifactModel[]>_.values(response);
522 toscaArtifactsFound.forEach(value => this.toscaArtifactTypes.push(new DropdownValue(value, value.type)));
525 this.notification.error({
526 message: 'Failed to Load Tosca Artifacts:' + error,
532 private loadInterfaceTypes() {
533 this.componentServiceNg2.getInterfaceTypes(this.component).subscribe(response => {
535 console.info("loadInterfaceTypes ", response);
536 for (const interfaceType in response) {
537 this.interfaceTypesMap.set(interfaceType, response[interfaceType]);
538 this.interfaceTypesTest.push(new DropdownValue(interfaceType, interfaceType));
542 this.notification.error({
543 message: 'Failed to Load Interface Types:' + error,
549 collapseAll(value: boolean = true): void {
550 this.interfaces.forEach(interfaceData => {
551 interfaceData.isCollapsed = value;
555 isAllCollapsed(): boolean {
556 return this.interfaces.every((interfaceData) => interfaceData.isCollapsed);
559 isAllExpanded(): boolean {
560 return this.interfaces.every((interfaceData) => !interfaceData.isCollapsed);
563 isInterfaceListEmpty(): boolean {
564 return this.interfaces.length === 0;
567 isOperationListEmpty(): boolean {
568 return this.interfaces.filter((interfaceData) => interfaceData.operations && interfaceData.operations.length > 0).length > 0;
571 onRemoveOperation(operation: OperationModel): void {
576 const deleteButton: IModalButtonComponent = {
578 text: this.modalTranslation.DELETE_BUTTON,
583 this.ComponentServiceNg2
584 .deleteInterfaceOperation(this.component, operation)
586 const curInterf = this.interfaces.find((interf) => interf.type === operation.interfaceType);
587 const index = curInterf.operations.findIndex((el) => el.uniqueId === operation.uniqueId);
588 curInterf.operations.splice(index, 1);
589 if (!curInterf.operations.length) {
590 const interfIndex = this.interfaces.findIndex((interf) => interf.type === operation.interfaceType);
591 this.interfaces.splice(interfIndex, 1);
597 const cancelButton: IModalButtonComponent = {
599 text: this.modalTranslation.CANCEL_BUTTON,
604 this.openOperation = null;
608 this.ModalServiceSdcUI.openWarningModal(
609 this.modalTranslation.DELETE_TITLE,
610 this.modalTranslation.deleteText(operation.name),
611 'deleteOperationModal',
612 [deleteButton, cancelButton],
616 private createOperation = (operation: OperationModel): void => {
617 this.ComponentServiceNg2.createInterfaceOperation(this.component, operation).subscribe((response: OperationModel) => {
618 this.openOperation = null;
620 let curInterf = this.interfaces.find((interf) => interf.type === operation.interfaceType);
623 curInterf = new UIInterfaceModel({
624 type: response.interfaceType,
625 uniqueId: response.uniqueId,
628 this.interfaces.push(curInterf);
631 const newOpModel = new UIOperationModel(response);
632 curInterf.operations.push(newOpModel);
633 this.sortInterfaces();
635 if (operation.workflowAssociationType === WORKFLOW_ASSOCIATION_OPTIONS.EXTERNAL && operation.artifactData) {
636 this.ComponentServiceNg2.uploadInterfaceOperationArtifact(this.component, newOpModel, operation).subscribe();
637 } else if (response.workflowId && operation.workflowAssociationType === WORKFLOW_ASSOCIATION_OPTIONS.EXISTING) {
638 this.WorkflowServiceNg2.associateWorkflowArtifact(this.component, response).subscribe();
639 } else if (operation.workflowAssociationType === WORKFLOW_ASSOCIATION_OPTIONS.NEW) {
640 this.$state.go('workspace.plugins', {path: 'workflowDesigner'});
645 private enableOrDisableSaveButton = (shouldEnable: boolean): void => {
646 const saveButton = this.modalInstance.instance.dynamicContent.getButtonById('saveButton');
647 saveButton.disabled = !shouldEnable;
650 private sortInterfaces(): void {
651 this.interfaces = this.interfaces.filter((interf) => interf.operations && interf.operations.length > 0); // remove empty interfaces
652 this.interfaces.sort((a, b) => a.type.localeCompare(b.type)); // sort interfaces alphabetically
653 this.interfaces.forEach((interf) => {
654 interf.operations.sort((a, b) => a.name.localeCompare(b.name)); // sort operations alphabetically