Implement adding Interface to VFC
[sdc.git] / catalog-ui / src / app / ng2 / pages / interface-operation / interface-operation.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 * as _ from "lodash";
22 import {Component, Inject, Input} from '@angular/core';
23 import {Component as IComponent} from 'app/models/components/component';
24
25 import {ISdcConfig, SdcConfigToken} from "app/ng2/config/sdc-config.config";
26 import {TranslateService} from "app/ng2/shared/translator/translate.service";
27
28 import {Observable} from "rxjs/Observable";
29
30 import {ModalComponent} from 'onap-ui-angular/dist/modals/modal.component';
31 import {ModalService} from 'app/ng2/services/modal.service';
32 import {
33     CapabilitiesGroup,
34     Capability,
35     InputBEModel,
36     InterfaceModel,
37     OperationModel,
38     WORKFLOW_ASSOCIATION_OPTIONS
39 } from 'app/models';
40
41 import {ComponentServiceNg2} from 'app/ng2/services/component-services/component.service';
42 import {PluginsService} from 'app/ng2/services/plugins.service';
43 import {WorkflowServiceNg2} from 'app/ng2/services/workflow.service';
44
45 import {
46     OperationCreatorComponent,
47     OperationCreatorInput
48 } from 'app/ng2/pages/interface-operation/operation-creator/operation-creator.component';
49 import {IModalButtonComponent, IModalConfig, SdcUiServices} from 'onap-ui-angular';
50
51 export class UIOperationModel extends OperationModel {
52     isCollapsed: boolean = true;
53     isEllipsis: boolean;
54     MAX_LENGTH = 75;
55     _description: string;
56
57     constructor(operation: OperationModel) {
58         super(operation);
59
60         if (!operation.description) {
61             this.description = '';
62         }
63
64         if (this.description.length > this.MAX_LENGTH) {
65             this.isEllipsis = true;
66         } else {
67             this.isEllipsis = false;
68         }
69     }
70
71     getDescriptionEllipsis(): string {
72         if (this.isCollapsed && this.description.length > this.MAX_LENGTH) {
73             return this.description.substr(0, this.MAX_LENGTH - 3) + '...';
74         }
75         return this.description;
76     }
77
78     toggleCollapsed(e) {
79         e.stopPropagation();
80         this.isCollapsed = !this.isCollapsed;
81     }
82 }
83
84 // tslint:disable-next-line:max-classes-per-file
85 class ModalTranslation {
86     CREATE_TITLE: string;
87     EDIT_TITLE: string;
88     DELETE_TITLE: string;
89     CANCEL_BUTTON: string;
90     SAVE_BUTTON: string;
91     CREATE_BUTTON: string;
92     DELETE_BUTTON: string;
93     deleteText: Function;
94
95     constructor(private TranslateService: TranslateService) {
96         this.TranslateService.languageChangedObservable.subscribe(lang => {
97             this.CREATE_TITLE = this.TranslateService.translate("INTERFACE_CREATE_TITLE");
98             this.EDIT_TITLE = this.TranslateService.translate('INTERFACE_EDIT_TITLE');
99             this.DELETE_TITLE = this.TranslateService.translate("INTERFACE_DELETE_TITLE");
100             this.CANCEL_BUTTON = this.TranslateService.translate("INTERFACE_CANCEL_BUTTON");
101             this.SAVE_BUTTON = this.TranslateService.translate("INTERFACE_SAVE_BUTTON");
102             this.CREATE_BUTTON = this.TranslateService.translate("INTERFACE_CREATE_BUTTON");
103             this.DELETE_BUTTON = this.TranslateService.translate("INTERFACE_DELETE_BUTTON");
104             this.deleteText = (operationName) => this.TranslateService.translate("INTERFACE_DELETE_TEXT", {operationName});
105         });
106     }
107 }
108
109 // tslint:disable-next-line:max-classes-per-file
110 export class UIInterfaceModel extends InterfaceModel {
111     isCollapsed: boolean = false;
112
113     constructor(interf?: any) {
114         super(interf);
115         this.operations = _.map(
116             this.operations,
117             (operation) => new UIOperationModel(operation)
118         );
119     }
120
121     toggleCollapse() {
122         this.isCollapsed = !this.isCollapsed;
123     }
124 }
125
126 // tslint:disable-next-line:max-classes-per-file
127 @Component({
128     selector: 'interface-operation',
129     templateUrl: './interface-operation.page.component.html',
130     styleUrls: ['interface-operation.page.component.less'],
131     providers: [ModalService, TranslateService]
132 })
133
134 export class InterfaceOperationComponent {
135
136     interfaces: UIInterfaceModel[];
137     modalInstance: ModalComponent;
138     openOperation: OperationModel;
139     enableWorkflowAssociation: boolean;
140     inputs: InputBEModel[];
141     isLoading: boolean;
142     interfaceTypes: { [interfaceType: string]: string[] };
143     modalTranslation: ModalTranslation;
144     workflowIsOnline: boolean;
145     workflows: any[];
146     capabilities: CapabilitiesGroup;
147
148     @Input() component: IComponent;
149     @Input() readonly: boolean;
150     @Input() enableMenuItems: Function;
151     @Input() disableMenuItems: Function;
152
153     constructor(
154         @Inject(SdcConfigToken) private sdcConfig: ISdcConfig,
155         @Inject("$state") private $state: ng.ui.IStateService,
156         private TranslateService: TranslateService,
157         private PluginsService: PluginsService,
158         private ComponentServiceNg2: ComponentServiceNg2,
159         private WorkflowServiceNg2: WorkflowServiceNg2,
160         private ModalServiceNg2: ModalService,
161         private ModalServiceSdcUI: SdcUiServices.ModalService
162     ) {
163         this.enableWorkflowAssociation = sdcConfig.enableWorkflowAssociation;
164         this.modalTranslation = new ModalTranslation(TranslateService);
165     }
166
167     ngOnInit(): void {
168         this.isLoading = true;
169         this.workflowIsOnline = !_.isUndefined(this.PluginsService.getPluginByStateUrl('workflowDesigner'));
170         Observable.forkJoin(
171             this.ComponentServiceNg2.getInterfaceOperations(this.component),
172             this.ComponentServiceNg2.getComponentInputs(this.component),
173             this.ComponentServiceNg2.getInterfaceTypes(this.component),
174             this.ComponentServiceNg2.getCapabilitiesAndRequirements(this.component.componentType, this.component.uniqueId)
175         ).subscribe((response: any[]) => {
176             const callback = (workflows) => {
177                 this.isLoading = false;
178                 this.initInterfaces(response[0].interfaces);
179                 this.sortInterfaces();
180                 this.inputs = response[1].inputs;
181                 this.interfaceTypes = response[2];
182                 this.workflows = (workflows.items) ? workflows.items : workflows;
183                 this.capabilities = response[3].capabilities;
184             };
185             if (this.enableWorkflowAssociation && this.workflowIsOnline) {
186                 this.WorkflowServiceNg2.getWorkflows().subscribe(
187                     callback,
188                     (err) => {
189                         this.workflowIsOnline = false;
190                         callback([]);
191                     }
192                 );
193             } else {
194                 callback([]);
195             }
196         });
197     }
198
199     initInterfaces(interfaces: InterfaceModel[]): void {
200         this.interfaces = _.map(interfaces, (interf) => new UIInterfaceModel(interf));
201     }
202
203     sortInterfaces(): void {
204         this.interfaces = _.filter(this.interfaces, (interf) => interf.operations && interf.operations.length > 0); // remove empty interfaces
205         this.interfaces.sort((a, b) => a.type.localeCompare(b.type)); // sort interfaces alphabetically
206         _.forEach(this.interfaces, (interf) => {
207             interf.operations.sort((a, b) => a.name.localeCompare(b.name)); // sort operations alphabetically
208         });
209     }
210
211     collapseAll(value: boolean = true): void {
212         _.forEach(this.interfaces, (interf) => {
213             interf.isCollapsed = value;
214         });
215     }
216
217     isAllCollapsed(): boolean {
218         return _.every(this.interfaces, (interf) => interf.isCollapsed);
219     }
220
221     isAllExpanded(): boolean {
222         return _.every(this.interfaces, (interf) => !interf.isCollapsed);
223     }
224
225     isListEmpty(): boolean {
226         return _.filter(
227             this.interfaces,
228             (interf) => interf.operations && interf.operations.length > 0
229         ).length === 0;
230     }
231
232     getDisabled = (): boolean => {
233         return !this.modalInstance.innerModalContent.instance.checkFormValidForSubmit();
234     }
235
236     onEditOperation = (operation?: OperationModel): void => {
237
238         const modalMap = {
239             create: {
240                 modalTitle: this.modalTranslation.CREATE_TITLE,
241                 saveBtnText: this.modalTranslation.CREATE_BUTTON,
242                 submitCallback: this.createOperation,
243             },
244             edit: {
245                 modalTitle: this.modalTranslation.EDIT_TITLE,
246                 saveBtnText: this.modalTranslation.SAVE_BUTTON,
247                 submitCallback: this.updateOperation,
248             }
249         };
250
251         const modalData = operation ? modalMap.edit : modalMap.create;
252
253         if (this.openOperation) {
254             if (operation ? operation.uniqueId === this.openOperation.uniqueId : !this.openOperation.uniqueId) {
255                 operation = this.openOperation;
256             }
257         }
258
259         const cancelButton: IModalButtonComponent = {
260             id: 'cancelButton',
261             text: this.modalTranslation.CANCEL_BUTTON,
262             type: 'secondary',
263             size: 'small',
264             closeModal: true,
265             callback: () => {
266                 this.openOperation = null;
267             },
268         };
269
270         const saveButton: IModalButtonComponent = {
271             id: 'saveButton',
272             text: modalData.saveBtnText,
273             type: 'primary',
274             size: 'small',
275             closeModal: true,
276             callback: () => {
277                 const modalInstance = this.modalInstance.innerModalContent.instance;
278
279                 const {operation, isUsingExistingWF, createParamLists} = modalInstance;
280                 createParamLists();
281                 this.openOperation = {...operation};
282
283                 if (this.enableWorkflowAssociation && !isUsingExistingWF()) {
284                     operation.workflowId = null;
285                     operation.workflowVersionId = null;
286                 }
287
288                 modalData.submitCallback(operation);
289             }
290         };
291
292         const input: OperationCreatorInput = {
293             allWorkflows: this.workflows,
294             inputOperation: operation,
295             interfaces: this.interfaces,
296             inputProperties: this.inputs,
297             enableWorkflowAssociation: this.enableWorkflowAssociation,
298             readonly: this.readonly,
299             interfaceTypes: this.interfaceTypes,
300             validityChangedCallback: this.enableOrDisableSaveButton,
301             workflowIsOnline: this.workflowIsOnline,
302             capabilities: _.filter(CapabilitiesGroup.getFlattenedCapabilities(this.capabilities), (capability: Capability) => capability.ownerId === this.component.uniqueId)
303         };
304
305         const modalConfig: IModalConfig = {
306             title: modalData.modalTitle,
307             size: 'l',
308             type: 'custom',
309             buttons: [saveButton, cancelButton] as IModalButtonComponent[]
310         };
311
312         this.modalInstance = this.ModalServiceSdcUI.openCustomModal(modalConfig, OperationCreatorComponent, input);
313     }
314
315     onRemoveOperation = (event: Event, operation: OperationModel): void => {
316         event.stopPropagation();
317
318         const deleteButton: IModalButtonComponent = {
319             id: 'deleteButton',
320             text: this.modalTranslation.DELETE_BUTTON,
321             type: 'primary',
322             size: 'small',
323             closeModal: true,
324             callback: () => {
325                 this.ComponentServiceNg2
326                 .deleteInterfaceOperation(this.component, operation)
327                 .subscribe(() => {
328                     const curInterf = _.find(this.interfaces, (interf) => interf.type === operation.interfaceType);
329                     const index = _.findIndex(curInterf.operations, (el) => el.uniqueId === operation.uniqueId);
330                     curInterf.operations.splice(index, 1);
331                     if (!curInterf.operations.length) {
332                         const interfIndex = _.findIndex(this.interfaces, (interf) => interf.type === operation.interfaceType);
333                         this.interfaces.splice(interfIndex, 1);
334                     }
335                 });
336             }
337         };
338
339         const cancelButton: IModalButtonComponent = {
340             id: 'cancelButton',
341             text: this.modalTranslation.CANCEL_BUTTON,
342             type: 'secondary',
343             size: 'small',
344             closeModal: true,
345             callback: () => {
346                 this.openOperation = null;
347             },
348         };
349
350         this.ModalServiceSdcUI.openWarningModal(
351             this.modalTranslation.DELETE_TITLE,
352             this.modalTranslation.deleteText(operation.name),
353             'deleteOperationModal',
354             [deleteButton, cancelButton],
355         );
356     }
357
358     private enableOrDisableSaveButton = (shouldEnable: boolean): void => {
359         const saveButton = this.modalInstance.getButtonById('saveButton');
360         saveButton.disabled = !shouldEnable;
361     }
362
363     private createOperation = (operation: OperationModel): void => {
364         this.ComponentServiceNg2.createInterfaceOperation(this.component, operation).subscribe((response: OperationModel) => {
365             this.openOperation = null;
366
367             let curInterf = _.find(
368                 this.interfaces,
369                 (interf) => interf.type === operation.interfaceType
370             );
371
372             if (!curInterf) {
373                 curInterf = new UIInterfaceModel({
374                     type: response.interfaceType,
375                     uniqueId: response.uniqueId,
376                     operations: []
377                 });
378                 this.interfaces.push(curInterf);
379             }
380
381             const newOpModel = new UIOperationModel(response);
382             curInterf.operations.push(newOpModel);
383             this.sortInterfaces();
384
385             if (operation.workflowAssociationType === WORKFLOW_ASSOCIATION_OPTIONS.EXTERNAL && operation.artifactData) {
386                 this.ComponentServiceNg2.uploadInterfaceOperationArtifact(this.component, newOpModel, operation).subscribe();
387             } else if (response.workflowId && operation.workflowAssociationType === WORKFLOW_ASSOCIATION_OPTIONS.EXISTING) {
388                 this.WorkflowServiceNg2.associateWorkflowArtifact(this.component, response).subscribe();
389             } else if (operation.workflowAssociationType === WORKFLOW_ASSOCIATION_OPTIONS.NEW) {
390                 this.$state.go('workspace.plugins', {path: 'workflowDesigner'});
391             }
392         });
393     }
394
395     private updateOperation = (operation: OperationModel): void => {
396         this.ComponentServiceNg2.updateInterfaceOperation(this.component, operation).subscribe((newOperation: OperationModel) => {
397             this.openOperation = null;
398
399             let oldOpIndex;
400             let oldInterf;
401             _.forEach(this.interfaces, (interf) => {
402                 _.forEach(interf.operations, (op) => {
403                     if (op.uniqueId === newOperation.uniqueId) {
404                         oldInterf = interf;
405                         oldOpIndex = _.findIndex(interf.operations, (el) => el.uniqueId === op.uniqueId);
406                     }
407                 })
408             });
409             oldInterf.operations.splice(oldOpIndex, 1);
410
411             const newInterf = _.find(this.interfaces, (interf) => interf.type === operation.interfaceType);
412             const newOpModel = new UIOperationModel(newOperation);
413             newInterf.operations.push(newOpModel);
414             this.sortInterfaces();
415
416             if (operation.workflowAssociationType === WORKFLOW_ASSOCIATION_OPTIONS.EXTERNAL && operation.artifactData) {
417                 this.ComponentServiceNg2.uploadInterfaceOperationArtifact(this.component, newOpModel, operation).subscribe();
418             } else if (newOperation.workflowId && operation.workflowAssociationType === WORKFLOW_ASSOCIATION_OPTIONS.EXISTING) {
419                 this.WorkflowServiceNg2.associateWorkflowArtifact(this.component, newOperation).subscribe();
420             }
421         });
422     }
423
424 }