2a25ad90df349c4be9e07f9b34a20c835d74b07c
[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
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     ModalModel,
38     OperationModel,
39     WORKFLOW_ASSOCIATION_OPTIONS
40 } from 'app/models';
41
42 import {ComponentServiceNg2} from 'app/ng2/services/component-services/component.service';
43 import {TopologyTemplateService} from "../../services/component-services/topology-template.service";
44 import {InterfaceOperationModel} from "../../../models/interfaceOperation";
45 import {
46     InterfaceOperationHandlerComponent
47 } from "../composition/interface-operatons/operation-creator/interface-operation-handler.component";
48 import {
49     DropdownValue
50 } from "../../components/ui/form-components/dropdown/ui-element-dropdown.component";
51 import {ToscaArtifactModel} from "../../../models/toscaArtifact";
52 import {ToscaArtifactService} from "../../services/tosca-artifact.service";
53 import {
54     InterfaceOperationComponent
55 } from "../interface-operation/interface-operation.page.component";
56 import {Observable} from "rxjs/Observable";
57 import {PluginsService} from 'app/ng2/services/plugins.service';
58
59 export class UIOperationModel extends OperationModel {
60     isCollapsed: boolean = true;
61     isEllipsis: boolean;
62     MAX_LENGTH = 75;
63
64     constructor(operation: OperationModel) {
65         super(operation);
66         if (!operation.description) {
67             this.description = '';
68         }
69
70         if (this.description.length > this.MAX_LENGTH) {
71             this.isEllipsis = true;
72         } else {
73             this.isEllipsis = false;
74         }
75     }
76
77     getDescriptionEllipsis(): string {
78         if (this.isCollapsed && this.description.length > this.MAX_LENGTH) {
79             return this.description.substr(0, this.MAX_LENGTH - 3) + '...';
80         }
81         return this.description;
82     }
83
84     toggleCollapsed(e) {
85         e.stopPropagation();
86         this.isCollapsed = !this.isCollapsed;
87     }
88 }
89
90 // tslint:disable-next-line:max-classes-per-file
91 class ModalTranslation {
92     CREATE_TITLE: string;
93     EDIT_TITLE: string;
94     DELETE_TITLE: string;
95     CANCEL_BUTTON: string;
96     SAVE_BUTTON: string;
97     CREATE_BUTTON: string;
98     DELETE_BUTTON: string;
99     deleteText: Function;
100
101     constructor(private TranslateService: TranslateService) {
102         this.TranslateService.languageChangedObservable.subscribe(lang => {
103             this.CREATE_TITLE = this.TranslateService.translate("INTERFACE_CREATE_TITLE");
104             this.EDIT_TITLE = this.TranslateService.translate('INTERFACE_EDIT_TITLE');
105             this.DELETE_TITLE = this.TranslateService.translate("INTERFACE_DELETE_TITLE");
106             this.CANCEL_BUTTON = this.TranslateService.translate("INTERFACE_CANCEL_BUTTON");
107             this.SAVE_BUTTON = this.TranslateService.translate("INTERFACE_SAVE_BUTTON");
108             this.CREATE_BUTTON = this.TranslateService.translate("INTERFACE_CREATE_BUTTON");
109             this.DELETE_BUTTON = this.TranslateService.translate("INTERFACE_DELETE_BUTTON");
110             this.deleteText = (operationName) => this.TranslateService.translate("INTERFACE_DELETE_TEXT", {operationName});
111         });
112     }
113 }
114
115 export class UIInterfaceModel extends InterfaceModel {
116     isCollapsed: boolean = false;
117
118     constructor(interf?: any) {
119         super(interf);
120         if (this.operations) {
121             this.operations = this.operations.map((operation) => new UIOperationModel(operation));
122         }
123     }
124
125     toggleCollapse() {
126         this.isCollapsed = !this.isCollapsed;
127     }
128 }
129
130 // tslint:disable-next-line:max-classes-per-file
131 @Component({
132     selector: 'interface-definition',
133     templateUrl: './interface-definition.page.component.html',
134     styleUrls: ['interface-definition.page.component.less'],
135     providers: [ModalService, TranslateService, InterfaceOperationComponent]
136 })
137 export class InterfaceDefinitionComponent {
138
139     modalInstance: ComponentRef<ModalComponent>;
140     interfaces: UIInterfaceModel[];
141     inputs: InputBEModel[];
142
143     deploymentArtifactsFilePath: Array<DropdownValue> = [];
144
145     toscaArtifactTypes: Array<DropdownValue> = [];
146     interfaceTypesTest: Array<DropdownValue> = [];
147     interfaceTypesMap: Map<string, string[]>;
148
149     isLoading: boolean;
150     interfaceTypes: { [interfaceType: string]: string[] };
151     modalTranslation: ModalTranslation;
152     workflows: any[];
153     capabilities: CapabilitiesGroup;
154     isViewOnly: boolean;
155
156     openOperation: OperationModel;
157     enableWorkflowAssociation: boolean;
158     workflowIsOnline: boolean;
159
160     @Input() component: IComponent;
161     @Input() readonly: boolean;
162     @Input() enableMenuItems: Function;
163     @Input() disableMenuItems: Function;
164
165     constructor(
166         @Inject(SdcConfigToken) private sdcConfig: ISdcConfig,
167         @Inject("$state") private $state: ng.ui.IStateService,
168         @Inject("Notification") private notification: any,
169         private translateService: TranslateService,
170         private componentServiceNg2: ComponentServiceNg2,
171         private modalServiceNg2: ModalService,
172         private modalServiceSdcUI: SdcUiServices.ModalService,
173         private topologyTemplateService: TopologyTemplateService,
174         private toscaArtifactService: ToscaArtifactService,
175         private ComponentServiceNg2: ComponentServiceNg2,
176         private WorkflowServiceNg2: WorkflowServiceNg2,
177         private ModalServiceSdcUI: SdcUiServices.ModalService,
178         private PluginsService: PluginsService
179     ) {
180         this.modalTranslation = new ModalTranslation(translateService);
181         this.interfaceTypesMap = new Map<string, string[]>();
182     }
183
184     ngOnInit(): void {
185         this.isLoading = true;
186         this.interfaces = [];
187         this.workflowIsOnline = !_.isUndefined(this.PluginsService.getPluginByStateUrl('workflowDesigner'));
188         Observable.forkJoin(
189             this.ComponentServiceNg2.getInterfaceOperations(this.component),
190             this.ComponentServiceNg2.getComponentInputs(this.component),
191             this.ComponentServiceNg2.getInterfaceTypes(this.component),
192             this.ComponentServiceNg2.getCapabilitiesAndRequirements(this.component.componentType, this.component.uniqueId)
193         ).subscribe((response: any[]) => {
194             const callback = (workflows) => {
195                 this.isLoading = false;
196                 this.initInterfaces(response[0].interfaces);
197                 this.sortInterfaces();
198                 this.inputs = response[1].inputs;
199                 this.interfaceTypes = response[2];
200                 this.workflows = (workflows.items) ? workflows.items : workflows;
201                 this.capabilities = response[3].capabilities;
202             };
203             if (this.enableWorkflowAssociation && this.workflowIsOnline) {
204                 this.WorkflowServiceNg2.getWorkflows().subscribe(
205                     callback,
206                     (err) => {
207                         this.workflowIsOnline = false;
208                         callback([]);
209                     }
210                 );
211             } else {
212                 callback([]);
213             }
214         });
215         this.loadToscaArtifacts();
216     }
217
218     initInterfaces(interfaces: InterfaceModel[]): void {
219         if (interfaces) {
220             this.interfaces = interfaces.map((interf) => new UIInterfaceModel(interf));
221         }
222     }
223
224     private cancelAndCloseModal = () => {
225         return this.modalServiceNg2.closeCurrentModal();
226     }
227
228     private disableSaveButton = (): boolean => {
229         return this.isViewOnly ||
230             (this.isEnableAddArtifactImplementation()
231                 && (!this.modalInstance.instance.dynamicContent.toscaArtifactTypeSelected ||
232                     !this.modalInstance.instance.dynamicContent.artifactName)
233             );
234     }
235
236     onSelectInterfaceOperation(interfaceModel: UIInterfaceModel, operation: InterfaceOperationModel) {
237         const isEdit = operation !== undefined;
238         const cancelButton: ButtonModel = new ButtonModel(this.modalTranslation.CANCEL_BUTTON, 'outline white', this.cancelAndCloseModal);
239         const saveButton: ButtonModel = new ButtonModel(this.modalTranslation.SAVE_BUTTON, 'blue',
240             () => isEdit ? this.updateOperation() : this.createOperationCallback(),
241             this.disableSaveButton
242         );
243         const interfaceDataModal: ModalModel =
244             new ModalModel('l', this.modalTranslation.EDIT_TITLE, '', [saveButton, cancelButton], 'custom');
245         this.modalInstance = this.modalServiceNg2.createCustomModal(interfaceDataModal);
246
247         this.modalServiceNg2.addDynamicContentToModal(
248             this.modalInstance,
249             InterfaceOperationHandlerComponent,
250             {
251                 deploymentArtifactsFilePath: this.deploymentArtifactsFilePath,
252                 toscaArtifactTypes: this.toscaArtifactTypes,
253                 selectedInterface: interfaceModel ? interfaceModel : new UIInterfaceModel(),
254                 selectedInterfaceOperation: operation ? operation : new InterfaceOperationModel(),
255                 validityChangedCallback: this.disableSaveButton,
256                 isViewOnly: this.isViewOnly,
257                 isEdit: isEdit,
258                 interfaceTypesMap: this.interfaceTypesMap,
259                 modelName: this.component.model
260             }
261         );
262         this.modalInstance.instance.open();
263     }
264
265     private updateOperation = (): void => {
266         this.modalServiceNg2.currentModal.instance.dynamicContent.instance.isLoading = true;
267         const interfaceOperationHandlerComponentInstance: InterfaceOperationHandlerComponent = this.modalInstance.instance.dynamicContent.instance;
268         const operationToUpdate = this.modalInstance.instance.dynamicContent.instance.operationToUpdate;
269         const isArtifactChecked = interfaceOperationHandlerComponentInstance.enableAddArtifactImplementation;
270         if (!isArtifactChecked) {
271             const artifactName = interfaceOperationHandlerComponentInstance.artifactName ?
272                 interfaceOperationHandlerComponentInstance.artifactName : '';
273             operationToUpdate.implementation = new ArtifactModel({'artifactName': artifactName, 'artifactVersion': ''} as ArtifactModel);
274         }
275         this.componentServiceNg2.updateComponentInterfaceOperation(this.component.uniqueId, operationToUpdate)
276         .subscribe((newOperation: InterfaceOperationModel) => {
277             let oldOpIndex;
278             let oldInterf;
279             this.interfaces.forEach(interf => {
280                 interf.operations.forEach(op => {
281                     if (op.uniqueId === newOperation.uniqueId) {
282                         oldInterf = interf;
283                         oldOpIndex = interf.operations.findIndex((el) => el.uniqueId === op.uniqueId);
284                     }
285                 });
286             });
287             oldInterf.operations.splice(oldOpIndex, 1);
288             oldInterf.operations.push(new InterfaceOperationModel(newOperation));
289         }, error => {
290             this.modalServiceNg2.currentModal.instance.dynamicContent.instance.isLoading = false;
291         }, () => {
292             this.sortInterfaces();
293             this.modalServiceNg2.currentModal.instance.dynamicContent.instance.isLoading = false;
294             this.modalServiceNg2.closeCurrentModal();
295         });
296     }
297
298     private createOperationCallback(): void {
299         this.modalServiceNg2.currentModal.instance.dynamicContent.instance.isLoading = true;
300         const operationToUpdate = this.modalInstance.instance.dynamicContent.instance.operationToUpdate;
301         console.log('createOperationCallback', operationToUpdate);
302         console.log('this.component', this.component);
303         this.componentServiceNg2.createComponentInterfaceOperation(this.component.uniqueId, this.component.getTypeUrl(), operationToUpdate)
304         .subscribe((newOperation: InterfaceOperationModel) => {
305             const foundInterface = this.interfaces.find(value => value.type === newOperation.interfaceType);
306             if (foundInterface) {
307                 foundInterface.operations.push(new UIOperationModel(new OperationModel(newOperation)));
308             } else {
309                 const uiInterfaceModel = new UIInterfaceModel();
310                 uiInterfaceModel.type = newOperation.interfaceType;
311                 uiInterfaceModel.uniqueId = newOperation.interfaceType;
312                 uiInterfaceModel.operations = [];
313                 uiInterfaceModel.operations.push(new UIOperationModel(new OperationModel(newOperation)));
314                 this.interfaces.push(uiInterfaceModel);
315             }
316         }, error => {
317             this.modalServiceNg2.currentModal.instance.dynamicContent.instance.isLoading = false;
318         }, () => {
319             this.modalServiceNg2.currentModal.instance.dynamicContent.instance.isLoading = false;
320             this.modalServiceNg2.closeCurrentModal();
321         });
322     }
323
324     private handleEnableAddArtifactImplementation = (newOperation: InterfaceOperationModel): InterfaceOperationModel => {
325         if (!this.isEnableAddArtifactImplementation()) {
326             newOperation.implementation.artifactType = null;
327             newOperation.implementation.artifactVersion = null;
328         }
329         return newOperation;
330     }
331
332     private isEnableAddArtifactImplementation = (): boolean => {
333         return this.modalInstance.instance.dynamicContent.enableAddArtifactImplementation;
334     }
335
336     private initInterfaceDefinition() {
337         this.isLoading = true;
338         this.interfaces = [];
339         this.topologyTemplateService.getComponentInterfaceOperations(this.component.componentType, this.component.uniqueId)
340         .subscribe((response) => {
341             if (response.interfaces) {
342                 this.interfaces = response.interfaces.map((interfaceModel) => new UIInterfaceModel(interfaceModel));
343             }
344             this.isLoading = false;
345         });
346     }
347
348     private loadToscaArtifacts() {
349         this.toscaArtifactService.getToscaArtifacts(this.component.model).subscribe(response => {
350             if (response) {
351                 let toscaArtifactsFound = <ToscaArtifactModel[]>_.values(response);
352                 toscaArtifactsFound.forEach(value => this.toscaArtifactTypes.push(new DropdownValue(value, value.type)));
353             }
354         }, error => {
355             this.notification.error({
356                 message: 'Failed to Load Tosca Artifacts:' + error,
357                 title: 'Failure'
358             });
359         });
360     }
361
362     private loadInterfaceTypes() {
363         this.componentServiceNg2.getInterfaceTypes(this.component).subscribe(response => {
364             if (response) {
365                 console.info("loadInterfaceTypes ", response);
366                 for (const interfaceType in response) {
367                     this.interfaceTypesMap.set(interfaceType, response[interfaceType]);
368                     this.interfaceTypesTest.push(new DropdownValue(interfaceType, interfaceType));
369                 }
370             }
371         }, error => {
372             this.notification.error({
373                 message: 'Failed to Load Interface Types:' + error,
374                 title: 'Failure'
375             });
376         });
377     }
378
379     collapseAll(value: boolean = true): void {
380         this.interfaces.forEach(interfaceData => {
381             interfaceData.isCollapsed = value;
382         });
383     }
384
385     isAllCollapsed(): boolean {
386         return this.interfaces.every((interfaceData) => interfaceData.isCollapsed);
387     }
388
389     isAllExpanded(): boolean {
390         return this.interfaces.every((interfaceData) => !interfaceData.isCollapsed);
391     }
392
393     isInterfaceListEmpty(): boolean {
394         return this.interfaces.length === 0;
395     }
396
397     isOperationListEmpty(): boolean {
398         return this.interfaces.filter((interfaceData) => interfaceData.operations && interfaceData.operations.length > 0).length > 0;
399     }
400
401     onRemoveOperation(operation: OperationModel): void {
402         if (this.readonly) {
403             return;
404         }
405
406         const deleteButton: IModalButtonComponent = {
407             id: 'deleteButton',
408             text: this.modalTranslation.DELETE_BUTTON,
409             type: 'primary',
410             size: 'small',
411             closeModal: true,
412             callback: () => {
413                 this.ComponentServiceNg2
414                 .deleteInterfaceOperation(this.component, operation)
415                 .subscribe(() => {
416                     const curInterf = this.interfaces.find((interf) => interf.type === operation.interfaceType);
417                     const index = curInterf.operations.findIndex((el) => el.uniqueId === operation.uniqueId);
418                     curInterf.operations.splice(index, 1);
419                     if (!curInterf.operations.length) {
420                         const interfIndex = this.interfaces.findIndex((interf) => interf.type === operation.interfaceType);
421                         this.interfaces.splice(interfIndex, 1);
422                     }
423                 });
424             }
425         };
426
427         const cancelButton: IModalButtonComponent = {
428             id: 'cancelButton',
429             text: this.modalTranslation.CANCEL_BUTTON,
430             type: 'secondary',
431             size: 'small',
432             closeModal: true,
433             callback: () => {
434                 this.openOperation = null;
435             },
436         };
437
438         this.ModalServiceSdcUI.openWarningModal(
439             this.modalTranslation.DELETE_TITLE,
440             this.modalTranslation.deleteText(operation.name),
441             'deleteOperationModal',
442             [deleteButton, cancelButton],
443         );
444     }
445
446     private createOperation = (operation: OperationModel): void => {
447         this.ComponentServiceNg2.createInterfaceOperation(this.component, operation).subscribe((response: OperationModel) => {
448             this.openOperation = null;
449
450             let curInterf = this.interfaces.find((interf) => interf.type === operation.interfaceType);
451
452             if (!curInterf) {
453                 curInterf = new UIInterfaceModel({
454                     type: response.interfaceType,
455                     uniqueId: response.uniqueId,
456                     operations: []
457                 });
458                 this.interfaces.push(curInterf);
459             }
460
461             const newOpModel = new UIOperationModel(response);
462             curInterf.operations.push(newOpModel);
463             this.sortInterfaces();
464
465             if (operation.workflowAssociationType === WORKFLOW_ASSOCIATION_OPTIONS.EXTERNAL && operation.artifactData) {
466                 this.ComponentServiceNg2.uploadInterfaceOperationArtifact(this.component, newOpModel, operation).subscribe();
467             } else if (response.workflowId && operation.workflowAssociationType === WORKFLOW_ASSOCIATION_OPTIONS.EXISTING) {
468                 this.WorkflowServiceNg2.associateWorkflowArtifact(this.component, response).subscribe();
469             } else if (operation.workflowAssociationType === WORKFLOW_ASSOCIATION_OPTIONS.NEW) {
470                 this.$state.go('workspace.plugins', {path: 'workflowDesigner'});
471             }
472         });
473     }
474
475     private enableOrDisableSaveButton = (shouldEnable: boolean): void => {
476         const saveButton = this.modalInstance.instance.dynamicContent.getButtonById('saveButton');
477         saveButton.disabled = !shouldEnable;
478     }
479
480     private sortInterfaces(): void {
481         this.interfaces = this.interfaces.filter((interf) => interf.operations && interf.operations.length > 0); // remove empty interfaces
482         this.interfaces.sort((a, b) => a.type.localeCompare(b.type)); // sort interfaces alphabetically
483         this.interfaces.forEach((interf) => {
484             interf.operations.sort((a, b) => a.name.localeCompare(b.name)); // sort operations alphabetically
485         });
486     }
487
488 }