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