bugfix for operations screen
[sdc.git] / catalog-ui / src / app / ng2 / pages / interface-operation / operation-creator / operation-creator.component.ts
1 import * as _ from "lodash";
2 import {Component, ViewChild} from '@angular/core';
3
4 import {Subscription} from "rxjs/Subscription";
5
6 import {TranslateService} from "app/ng2/shared/translator/translate.service";
7 import {WorkflowServiceNg2} from 'app/ng2/services/workflow.service';
8 import {
9     InterfaceModel,
10     OperationModel,
11     OperationParameter,
12     InputBEModel,
13     WORKFLOW_ASSOCIATION_OPTIONS,
14     Capability
15 } from 'app/models';
16
17 import { Tabs } from "app/ng2/components/ui/tabs/tabs.component";
18 import { DropdownValue } from "app/ng2/components/ui/form-components/dropdown/ui-element-dropdown.component";
19 import { IDropDownOption } from 'onap-ui-angular';
20 import { DropDownComponent } from "onap-ui-angular/dist/components";
21 import { DROPDOWN_OPTION_TYPE } from "app/utils/constants";
22
23 export class DropDownOption implements IDropDownOption {
24     value: string;
25     label: string;
26
27     constructor(value: string, label?: string) {
28         this.value = value;
29         this.label = label || value;
30     }
31 }
32
33 class TypedDropDownOption extends DropDownOption {
34     type: string;
35
36     constructor(value: string, label?: string, type?: string) {
37         super(value, label);
38         this.type = type;
39     }
40 }
41
42 export interface OperationCreatorInput {
43     allWorkflows: Array<any>,
44     inputOperation: OperationModel,
45     interfaces: Array<InterfaceModel>,
46     inputProperties: Array<InputBEModel>,
47     enableWorkflowAssociation: boolean,
48     readonly: boolean,
49     interfaceTypes: { [interfaceType: string]: Array<string> },
50     validityChangedCallback: Function,
51     workflowIsOnline: boolean,
52     capabilities: Array<Capability>
53 }
54
55 @Component({
56     selector: 'operation-creator',
57     templateUrl: './operation-creator.component.html',
58     styleUrls: ['./operation-creator.component.less'],
59     providers: [TranslateService]
60 })
61
62 export class OperationCreatorComponent implements OperationCreatorInput {
63
64     input: OperationCreatorInput;
65     inputOperation: OperationModel;
66     interfaces: Array<InterfaceModel>;
67     operation: OperationModel;
68     interfaceNames: Array<TypedDropDownOption> = [];
69     interfaceTypes: { [interfaceType: string]: Array<string> };
70     operationNames: Array<TypedDropDownOption> = [];
71     validityChangedCallback: Function;
72     capabilities: Array<Capability>;
73
74     allWorkflows: Array<any>;
75     workflows: Array<DropdownValue> = [];
76     workflowVersions: Array<DropdownValue> = [];
77     inputProperties: Array<InputBEModel> = [];
78     archivedWorkflowId: string = '&';
79
80     inputParameters: Array<OperationParameter> = [];
81     noAssignInputParameters: Array<OperationParameter> = [];
82     assignInputParameters: { [key: string]: { [key: string]: Array<OperationParameter>; }; } = {};
83
84     outputParameters: Array<OperationParameter> = [];
85     noAssignOutputParameters: Array<OperationParameter> = [];
86     assignOutputParameters: { [key: string]: { [key: string]: Array<OperationParameter>; }; } = {};
87     componentCapabilities: Array<Capability> = [];
88
89     tableParameters: Array<OperationParameter> = [];
90     operationOutputs: Array<OperationModel> = [];
91
92     associationOptions: Array<DropdownValue> = [];
93     workflowAssociationType: string;
94
95     enableWorkflowAssociation: boolean;
96     workflowIsOnline: boolean;
97     isEditMode: boolean = false;
98     isLoading: boolean = false;
99     readonly: boolean;
100
101     propertyTooltipText: String;
102
103     TYPE_INPUT = 'Inputs';
104     TYPE_OUTPUT = 'Outputs';
105
106     INTERFACE_OTHER_HEADER = 'Local Interfaces';
107     INTERFACE_OTHER = 'Local';
108
109     @ViewChild('propertyInputTabs') propertyInputTabs: Tabs;
110     @ViewChild('operationNamesDropdown') operationNamesDropdown: DropDownComponent;
111     @ViewChild('workflowAssignmentDropdown') workflowAssignmentDropdown: DropDownComponent;
112     currentTab: String;
113
114     constructor(private workflowServiceNg2: WorkflowServiceNg2, private translateService: TranslateService) {
115         this.translateService.languageChangedObservable.subscribe(lang => {
116             this.propertyTooltipText = this.translateService.translate("OPERATION_PROPERTY_TOOLTIP_TEXT");
117
118             this.associationOptions = [
119                 new DropDownOption(WORKFLOW_ASSOCIATION_OPTIONS.EXTERNAL, this.translateService.translate("EXTERNAL_WORKFLOW_ASSOCIATION")),
120                 new DropDownOption(WORKFLOW_ASSOCIATION_OPTIONS.EXISTING, this.translateService.translate("EXISTING_WORKFLOW_ASSOCIATION")),
121             ];
122
123             this.workflowAssociationType = this.operation.workflowAssociationType;
124         });
125
126         this.currentTab = this.TYPE_INPUT;
127     }
128
129     createInterfaceDropdown(type: string) {
130         let label = type;
131         const lastDot = label.lastIndexOf('.');
132         if (lastDot > -1) {
133             label = label.substr(lastDot + 1);
134         }
135         return new TypedDropDownOption(type, label);
136     }
137
138     ngOnInit() {
139         this.interfaceNames = _.map(
140             _.keys(this.interfaceTypes),
141             type => this.createInterfaceDropdown(type)
142         );
143         this.interfaceNames.unshift(new TypedDropDownOption('Existing Interfaces', 'Existing Interfaces', DROPDOWN_OPTION_TYPE.HEADER));
144         this.interfaceNames = this.interfaceNames.concat([
145             new TypedDropDownOption(' ', ' ', DROPDOWN_OPTION_TYPE.HORIZONTAL_LINE),
146             new TypedDropDownOption(this.INTERFACE_OTHER_HEADER, this.INTERFACE_OTHER_HEADER, DROPDOWN_OPTION_TYPE.HEADER),
147             new TypedDropDownOption(this.INTERFACE_OTHER)
148         ]);
149         const inputOperation = this.inputOperation;
150         this.operation = new OperationModel(inputOperation || {});
151
152         this.operationOutputs = _.reduce(
153             this.interfaces,
154             (acc: Array<OperationModel>, interf) => [
155                 ...acc,
156                 ..._.filter(
157                     interf.operations,
158                     op => op.uniqueId !== this.operation.uniqueId
159                 ),
160             ],
161         []);
162
163         if (this.enableWorkflowAssociation) {
164             if (this.workflowIsOnline) {
165                 this.workflows = _.map(
166                     _.filter(
167                         this.allWorkflows,
168                         (workflow: any) => {
169                             if (workflow.archiving === this.workflowServiceNg2.WF_STATE_ACTIVE) {
170                                 return true;
171                             }
172                             if (workflow.archiving === this.workflowServiceNg2.WF_STATE_ARCHIVED &&
173                                 workflow.id === this.operation.workflowId) {
174                                 this.archivedWorkflowId = workflow.id;
175                                 return true;
176                             }
177                             return false;
178                         }
179                     ),
180                     (workflow: any) => new DropdownValue(workflow.id, workflow.name)
181                 );
182             } else {
183                 this.workflows = [];
184             }
185         }
186         this.reconstructOperation();
187         this.filterCapabilities();
188         this.validityChanged();
189         this.updateTable();
190     }
191
192     ngAfterViewInit() {
193         if(this.workflowAssignmentDropdown){
194             this.workflowAssignmentDropdown.allOptions = this.associationOptions && this.associationOptions.length ?
195                 this.associationOptions :
196                 [
197                     new DropDownOption(WORKFLOW_ASSOCIATION_OPTIONS.EXTERNAL, this.translateService.translate("EXTERNAL_WORKFLOW_ASSOCIATION")),
198                     new DropDownOption(WORKFLOW_ASSOCIATION_OPTIONS.EXISTING, this.translateService.translate("EXISTING_WORKFLOW_ASSOCIATION")),
199                 ];
200         }
201     }
202
203     reconstructOperation = () => {
204
205         const buildAndUpdate = () => {
206             this.buildParams();
207             this.updateTable();
208         };
209
210         const inputOperation = this.inputOperation;
211         if (inputOperation) {
212             this.onSelectInterface(new DropDownOption(this.operation.interfaceType));
213
214             this.operation.artifactFileName = this.operation.artifactFileName || this.operation.implementation.artifactName;
215
216             if (this.enableWorkflowAssociation && inputOperation.workflowVersionId && this.isUsingExistingWF(inputOperation)) {
217                 this.assignInputParameters[this.operation.workflowId] = {[inputOperation.workflowVersionId]: []};
218                 this.assignOutputParameters[this.operation.workflowId] = {[inputOperation.workflowVersionId]: []};
219                 this.inputParameters = this.assignInputParameters[this.operation.workflowId][this.operation.workflowVersionId];
220                 this.outputParameters = this.assignOutputParameters[this.operation.workflowId][this.operation.workflowVersionId];
221
222                 const sub = this.onSelectWorkflow(new DropDownOption(inputOperation.workflowId), inputOperation.workflowVersionId);
223                 if (sub) {
224                     sub.add(() => {
225                         buildAndUpdate();
226                         this.operation.workflowVersionId = '-1';
227                         setTimeout(() => this.operation.workflowVersionId = this.inputOperation.workflowVersionId);
228                     });
229                 } else {
230                     buildAndUpdate();
231                 }
232             } else {
233                 this.inputParameters = this.noAssignInputParameters;
234                 this.outputParameters = this.noAssignOutputParameters;
235                 buildAndUpdate();
236             }
237
238             if (inputOperation.uniqueId) {
239                 this.isEditMode = true;
240             }
241         }
242
243     }
244
245     filterCapabilities() {
246         this.componentCapabilities = _.filter(this.capabilities, (cap: Capability) => cap.properties);
247     }
248
249     buildParams = () => {
250
251         if (this.inputOperation.outputs) {
252             this.currentTab = this.TYPE_OUTPUT;
253             this.updateTable();
254             _.forEach(
255                 [...this.inputOperation.outputs.listToscaDataDefinition].sort((a, b) => a.name.localeCompare(b.name)),
256                 (output: OperationParameter) => {
257                     this.addParam({...output, required: Boolean(output.required)});
258                 }
259             );
260         }
261
262         this.currentTab = this.TYPE_INPUT;
263         this.updateTable();
264         if (this.inputOperation.inputs) {
265             _.forEach(
266                 [...this.inputOperation.inputs.listToscaDataDefinition].sort((a, b) => a.name.localeCompare(b.name)),
267                 (input: OperationParameter) => {
268                     this.addParam({...input, required: Boolean(input.required)});
269                 }
270             );
271         }
272
273     }
274
275     isInterfaceOther(): boolean {
276         return this.operation.interfaceType === this.INTERFACE_OTHER;
277     }
278
279     onSelectInterface(interf: IDropDownOption) {
280         if (interf && this.operation.interfaceType !== interf.value) {
281             this.operation.name = null;
282         }
283         this.operation.interfaceType = interf && interf.value;
284         this.operationNames = !this.operation.interfaceType ? [] : (
285             _.map(
286                 this.interfaceTypes[this.operation.interfaceType],
287                 name => {
288                     const curInterf = _.find(
289                         this.interfaces,
290                         interf => interf.type === this.operation.interfaceType
291                     );
292                     const existingOp = _.find(
293                         curInterf && curInterf.operations || [],
294                         op => op.name === name
295                     );
296                     const ddType = (existingOp && existingOp.uniqueId !== this.operation.uniqueId) ? DROPDOWN_OPTION_TYPE.HORIZONTAL_LINE : DROPDOWN_OPTION_TYPE.SIMPLE;
297                     return new TypedDropDownOption(name, name, ddType);
298                 }
299             )
300         );
301         if(this.operationNamesDropdown) {
302             this.operationNamesDropdown.allOptions = <IDropDownOption[]>this.operationNames;
303         }
304         this.validityChanged();
305     }
306
307     onSelectOperationName(name: IDropDownOption) {
308         if (name) {
309             this.operation.name = name.value;
310         }
311         this.validityChanged();
312     }
313
314     onChangeName() {
315         this.validityChanged();
316     }
317
318     get descriptionValue() {
319         return this.operation.description;
320     }
321
322     set descriptionValue(v) {
323         this.operation.description = v || null;
324         this.validityChanged();
325     }
326
327     onSelectWorkflow(workflowId: DropDownOption, selectedVersionId?: string): Subscription {
328
329         if (_.isUndefined(workflowId) || !this.workflowIsOnline) {
330             return;
331         }
332
333         if (this.operation.workflowId === workflowId.value && !selectedVersionId) {
334             return;
335         }
336
337         this.operation.workflowId = workflowId.value;
338         if (!this.assignInputParameters[this.operation.workflowId]) {
339             this.assignInputParameters[this.operation.workflowId] = {};
340             this.assignOutputParameters[this.operation.workflowId] = {};
341         }
342
343         this.isLoading = true;
344         this.validityChanged();
345         return this.workflowServiceNg2.getWorkflowVersions(this.operation.workflowId).subscribe((versions: Array<any>) => {
346             this.isLoading = false;
347
348             this.workflowVersions = _.map(
349                 _.filter(
350                     versions, version => version.state === this.workflowServiceNg2.VERSION_STATE_CERTIFIED
351                 ).sort((a, b) => a.name.localeCompare(b.name)),
352                 (version: any) => {
353                     if (!this.assignInputParameters[this.operation.workflowId][version.id] && version.id !== selectedVersionId) {
354                         this.assignInputParameters[this.operation.workflowId][version.id] = _.map(version.inputs, (input: any) => {
355                             return new OperationParameter({...input, type: input.type.toLowerCase(), required: Boolean(input.mandatory)});
356                         })
357                         .sort((a, b) => a.name.localeCompare(b.name));
358
359                         this.assignOutputParameters[this.operation.workflowId][version.id] = _.map(version.outputs, (output: any) => {
360                             return new OperationParameter({...output, type: output.type.toLowerCase(), required: Boolean(output.mandatory)});
361                         })
362                         .sort((a, b) => a.name.localeCompare(b.name));
363                     }
364                     return new DropdownValue(version.id, `V ${version.name}`);
365                 }
366             );
367             if (!selectedVersionId && this.workflowVersions.length) {
368                 this.operation.workflowVersionId = _.last(this.workflowVersions).value;
369             }
370
371             this.changeWorkflowVersion(new DropDownOption(this.operation.workflowVersionId));
372             this.validityChanged();
373         });
374
375     }
376
377     changeWorkflowVersion(versionId: DropDownOption) {
378
379         if (_.isUndefined(versionId) || !this.workflowIsOnline) {
380             return;
381         }
382
383         this.operation.workflowVersionId = versionId.value;
384         this.inputParameters = this.assignInputParameters[this.operation.workflowId][this.operation.workflowVersionId];
385         this.outputParameters = this.assignOutputParameters[this.operation.workflowId][this.operation.workflowVersionId];
386         this.updateTable();
387         this.validityChanged();
388
389     }
390
391     toggleAssociateWorkflow(type: DropDownOption) {
392
393         if (_.isUndefined(type)) {
394             return;
395         }
396
397         this.operation.workflowAssociationType = type.value;
398         this.workflowAssociationType = this.operation.workflowAssociationType;
399
400         if (!this.isUsingExistingWF()) {
401             this.inputParameters = this.noAssignInputParameters;
402             this.outputParameters = this.noAssignOutputParameters;
403         } else {
404             if (!this.operation.workflowId || !this.operation.workflowVersionId) {
405                 this.inputParameters = [];
406                 this.outputParameters = [];
407             } else {
408                 this.inputParameters = this.assignInputParameters[this.operation.workflowId][this.operation.workflowVersionId];
409                 this.outputParameters = this.assignOutputParameters[this.operation.workflowId][this.operation.workflowVersionId];
410             }
411         }
412
413         this.updateTable();
414         this.validityChanged();
415
416     }
417
418     onChangeArtifactFile(e: any) {
419         const file = e.target.files && e.target.files[0];
420         this.operation.artifactFileName = file && file.name;
421
422         if (!this.operation.artifactFileName) {
423             this.operation.artifactData = null;
424             this.validityChanged();
425             return;
426         }
427
428         const reader = new FileReader();
429         reader.onloadend = () => {
430             this.isLoading = false;
431             const result = <String>reader.result;
432             this.operation.artifactData = result.substring(result.indexOf(',') + 1);
433             this.validityChanged();
434         }
435
436         this.isLoading = true;
437         reader.readAsDataURL(file);
438     }
439
440     tabChanged = (event) => {
441
442         this.currentTab = event.title;
443         this.updateTable();
444
445     }
446
447     updateTable() {
448
449         switch (this.currentTab) {
450             case this.TYPE_INPUT:
451                 this.tableParameters = this.inputParameters;
452                 break;
453             case this.TYPE_OUTPUT:
454                 this.tableParameters = this.outputParameters;
455                 break;
456         }
457
458     }
459
460     addParam(param?: OperationParameter): void {
461         this.tableParameters.push(new OperationParameter(param || {required: false}));
462         this.validityChanged();
463     }
464
465     canAdd = (): boolean => {
466
467         let valid = true;
468         if (this.currentTab === this.TYPE_INPUT) {
469             _.forEach(this.inputParameters, param => {
470                 if (!param.name || !param.inputId) {
471                     valid = false;
472                 }
473             });
474         } else {
475             _.forEach(this.outputParameters, param => {
476                 if (!param.name || !param.type) {
477                     valid = false;
478                 }
479             });
480         }
481
482         return valid;
483
484     }
485
486     isParamsValid = (): boolean => {
487
488         let valid = true;
489         _.forEach(this.inputParameters, param => {
490             if (!param.name || !param.inputId) {
491                 valid = false;
492             }
493         });
494         _.forEach(this.outputParameters, param => {
495             if (!param.name || !param.type) {
496                 valid = false;
497             }
498         });
499
500         return valid;
501
502     }
503
504     onRemoveParam = (param: OperationParameter): void => {
505         let index = _.indexOf(this.tableParameters, param);
506         this.tableParameters.splice(index, 1);
507         this.validityChanged();
508     }
509
510     createParamLists = () => {
511         this.operation.createInputsList(this.inputParameters);
512         this.operation.createOutputsList(this.outputParameters);
513     }
514
515     isUsingExistingWF = (operation?: OperationModel): boolean => {
516         operation = operation || this.operation;
517         return operation.workflowAssociationType === WORKFLOW_ASSOCIATION_OPTIONS.EXISTING;
518     }
519
520     isUsingExternalWF = (operation?: OperationModel): boolean => {
521         operation = operation || this.operation;
522         return operation.workflowAssociationType === WORKFLOW_ASSOCIATION_OPTIONS.EXTERNAL;
523     }
524
525     shouldCreateWF = (operation?: OperationModel): boolean => {
526         operation = operation || this.operation;
527         return operation.workflowAssociationType === WORKFLOW_ASSOCIATION_OPTIONS.NEW;
528     }
529
530     checkFormValidForSubmit = (): boolean => {
531         return this.operation.name &&
532             (!this.isUsingExistingWF() || this.operation.workflowVersionId) &&
533             this.isParamsValid();
534     }
535
536     validityChanged = () => {
537         let validState = this.checkFormValidForSubmit();
538         this.validityChangedCallback(validState);
539     }
540
541     getSelectedDropdown(options: DropdownValue[], selectedValue: string): DropdownValue {
542         const selectedDropdown = _.find(options, (option) => option.value === selectedValue);
543         return selectedDropdown || this.toDropDownOption(null);
544     }
545
546     toDropDownOption(val: string) {
547         return { value : val, label: val };
548     }
549 }