Add UI support for adding tosca artifact types
[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             if (this.enableWorkflowAssociation && inputOperation.workflowVersionId && this.isUsingExistingWF(inputOperation)) {
215                 this.assignInputParameters[this.operation.workflowId] = {[inputOperation.workflowVersionId]: []};
216                 this.assignOutputParameters[this.operation.workflowId] = {[inputOperation.workflowVersionId]: []};
217                 this.inputParameters = this.assignInputParameters[this.operation.workflowId][this.operation.workflowVersionId];
218                 this.outputParameters = this.assignOutputParameters[this.operation.workflowId][this.operation.workflowVersionId];
219
220                 const sub = this.onSelectWorkflow(new DropDownOption(inputOperation.workflowId), inputOperation.workflowVersionId);
221                 if (sub) {
222                     sub.add(() => {
223                         buildAndUpdate();
224                         this.operation.workflowVersionId = '-1';
225                         setTimeout(() => this.operation.workflowVersionId = this.inputOperation.workflowVersionId);
226                     });
227                 } else {
228                     buildAndUpdate();
229                 }
230             } else {
231                 this.inputParameters = this.noAssignInputParameters;
232                 this.outputParameters = this.noAssignOutputParameters;
233                 buildAndUpdate();
234             }
235
236             if (inputOperation.uniqueId) {
237                 this.isEditMode = true;
238             }
239         }
240
241     }
242
243     filterCapabilities() {
244         this.componentCapabilities = _.filter(this.capabilities, (cap: Capability) => cap.properties);
245     }
246
247     buildParams = () => {
248
249         if (this.inputOperation.outputs) {
250             this.currentTab = this.TYPE_OUTPUT;
251             this.updateTable();
252             _.forEach(
253                 [...this.inputOperation.outputs.listToscaDataDefinition].sort((a, b) => a.name.localeCompare(b.name)),
254                 (output: OperationParameter) => {
255                     this.addParam({...output, required: Boolean(output.required)});
256                 }
257             );
258         }
259
260         this.currentTab = this.TYPE_INPUT;
261         this.updateTable();
262         if (this.inputOperation.inputs) {
263             _.forEach(
264                 [...this.inputOperation.inputs.listToscaDataDefinition].sort((a, b) => a.name.localeCompare(b.name)),
265                 (input: OperationParameter) => {
266                     this.addParam({...input, required: Boolean(input.required)});
267                 }
268             );
269         }
270
271     }
272
273     isInterfaceOther(): boolean {
274         return this.operation.interfaceType === this.INTERFACE_OTHER;
275     }
276
277     onSelectInterface(interf: IDropDownOption) {
278         if (interf && this.operation.interfaceType !== interf.value) {
279             this.operation.name = null;
280         }
281         this.operation.interfaceType = interf && interf.value;
282         this.operationNames = !this.operation.interfaceType ? [] : (
283             _.map(
284                 this.interfaceTypes[this.operation.interfaceType],
285                 name => {
286                     const curInterf = _.find(
287                         this.interfaces,
288                         interf => interf.type === this.operation.interfaceType
289                     );
290                     const existingOp = _.find(
291                         curInterf && curInterf.operations || [],
292                         op => op.name === name
293                     );
294                     const ddType = (existingOp && existingOp.uniqueId !== this.operation.uniqueId) ? DROPDOWN_OPTION_TYPE.HORIZONTAL_LINE : DROPDOWN_OPTION_TYPE.SIMPLE;
295                     return new TypedDropDownOption(name, name, ddType);
296                 }
297             )
298         );
299         if(this.operationNamesDropdown) {
300             this.operationNamesDropdown.allOptions = <IDropDownOption[]>this.operationNames;
301         }
302         this.validityChanged();
303     }
304
305     onSelectOperationName(name: IDropDownOption) {
306         if (name) {
307             this.operation.name = name.value;
308         }
309         this.validityChanged();
310     }
311
312     onChangeName() {
313         this.validityChanged();
314     }
315
316     get descriptionValue() {
317         return this.operation.description;
318     }
319
320     set descriptionValue(v) {
321         this.operation.description = v || null;
322         this.validityChanged();
323     }
324
325     onSelectWorkflow(workflowId: DropDownOption, selectedVersionId?: string): Subscription {
326
327         if (_.isUndefined(workflowId) || !this.workflowIsOnline) {
328             return;
329         }
330
331         if (this.operation.workflowId === workflowId.value && !selectedVersionId) {
332             return;
333         }
334
335         this.operation.workflowId = workflowId.value;
336         if (!this.assignInputParameters[this.operation.workflowId]) {
337             this.assignInputParameters[this.operation.workflowId] = {};
338             this.assignOutputParameters[this.operation.workflowId] = {};
339         }
340         this.operation.workflowName = workflowId.label;
341         if (!this.assignInputParameters[this.operation.workflowName]) {
342             this.assignInputParameters[this.operation.workflowName] = {};
343             this.assignOutputParameters[this.operation.workflowName] = {};
344         }
345
346         this.isLoading = true;
347         this.validityChanged();
348         return this.workflowServiceNg2.getWorkflowVersions(this.operation.workflowId).subscribe((versions: Array<any>) => {
349             this.isLoading = false;
350
351             this.workflowVersions = _.map(
352                 _.filter(
353                     versions, version => version.state === this.workflowServiceNg2.VERSION_STATE_CERTIFIED
354                 ).sort((a, b) => a.name.localeCompare(b.name)),
355                 (version: any) => {
356                     if (!this.assignInputParameters[this.operation.workflowId][version.id] && version.id !== selectedVersionId) {
357                         this.assignInputParameters[this.operation.workflowId][version.id] = _.map(version.inputs, (input: any) => {
358                             return new OperationParameter({...input, type: input.type.toLowerCase(), required: Boolean(input.mandatory)});
359                         })
360                         .sort((a, b) => a.name.localeCompare(b.name));
361
362                         this.assignOutputParameters[this.operation.workflowId][version.id] = _.map(version.outputs, (output: any) => {
363                             return new OperationParameter({...output, type: output.type.toLowerCase(), required: Boolean(output.mandatory)});
364                         })
365                         .sort((a, b) => a.name.localeCompare(b.name));
366                     }
367                     return new DropdownValue(version.id, `V ${version.name}`);
368                 }
369             );
370             if (!selectedVersionId && this.workflowVersions.length) {
371                 this.operation.workflowVersionId = _.last(this.workflowVersions).value;
372                 this.operation.workflowVersion = _.last(this.workflowVersions).label;
373             }
374
375             this.changeWorkflowVersion(new DropDownOption(this.operation.workflowVersionId));
376             this.validityChanged();
377         });
378
379     }
380
381     changeWorkflowVersion(versionId: DropDownOption) {
382
383         if (_.isUndefined(versionId) || !this.workflowIsOnline) {
384             return;
385         }
386
387         this.operation.workflowVersionId = versionId.value;
388         this.inputParameters = this.assignInputParameters[this.operation.workflowId][this.operation.workflowVersionId];
389         this.outputParameters = this.assignOutputParameters[this.operation.workflowId][this.operation.workflowVersionId];
390         this.updateTable();
391         this.validityChanged();
392
393     }
394
395     toggleAssociateWorkflow(type: DropDownOption) {
396
397         if (_.isUndefined(type)) {
398             return;
399         }
400
401         this.operation.workflowAssociationType = type.value;
402         this.workflowAssociationType = this.operation.workflowAssociationType;
403
404         if (!this.isUsingExistingWF()) {
405             this.inputParameters = this.noAssignInputParameters;
406             this.outputParameters = this.noAssignOutputParameters;
407         } else {
408             if (!this.operation.workflowId || !this.operation.workflowVersionId) {
409                 this.inputParameters = [];
410                 this.outputParameters = [];
411             } else {
412                 this.inputParameters = this.assignInputParameters[this.operation.workflowId][this.operation.workflowVersionId];
413                 this.outputParameters = this.assignOutputParameters[this.operation.workflowId][this.operation.workflowVersionId];
414             }
415         }
416
417         this.updateTable();
418         this.validityChanged();
419
420     }
421
422     onChangeArtifactFile(e: any) {
423         const file = e.target.files && e.target.files[0];
424         this.operation.artifactFileName = file && file.name;
425
426         if (!this.operation.artifactFileName) {
427             this.operation.artifactData = null;
428             this.validityChanged();
429             return;
430         }
431
432         const reader = new FileReader();
433         reader.onloadend = () => {
434             this.isLoading = false;
435             const result = <String>reader.result;
436             this.operation.artifactData = result.substring(result.indexOf(',') + 1);
437             this.validityChanged();
438         }
439
440         this.isLoading = true;
441         reader.readAsDataURL(file);
442     }
443
444     tabChanged = (event) => {
445
446         this.currentTab = event.title;
447         this.updateTable();
448
449     }
450
451     updateTable() {
452
453         switch (this.currentTab) {
454             case this.TYPE_INPUT:
455                 this.tableParameters = this.inputParameters;
456                 break;
457             case this.TYPE_OUTPUT:
458                 this.tableParameters = this.outputParameters;
459                 break;
460         }
461
462     }
463
464     addParam(param?: OperationParameter): void {
465         this.tableParameters.push(new OperationParameter(param || {required: false}));
466         this.validityChanged();
467     }
468
469     canAdd = (): boolean => {
470
471         let valid = true;
472         if (this.currentTab === this.TYPE_INPUT) {
473             _.forEach(this.inputParameters, param => {
474                 if (!param.name || !param.inputId) {
475                     valid = false;
476                 }
477             });
478         } else {
479             _.forEach(this.outputParameters, param => {
480                 if (!param.name || !param.type) {
481                     valid = false;
482                 }
483             });
484         }
485
486         return valid;
487
488     }
489
490     isParamsValid = (): boolean => {
491
492         let valid = true;
493         _.forEach(this.inputParameters, param => {
494             if (!param.name || !param.inputId) {
495                 valid = false;
496             }
497         });
498         _.forEach(this.outputParameters, param => {
499             if (!param.name || !param.type) {
500                 valid = false;
501             }
502         });
503
504         return valid;
505
506     }
507
508     onRemoveParam = (param: OperationParameter): void => {
509         let index = _.indexOf(this.tableParameters, param);
510         this.tableParameters.splice(index, 1);
511         this.validityChanged();
512     }
513
514     createParamLists = () => {
515         this.operation.createInputsList(this.inputParameters);
516         this.operation.createOutputsList(this.outputParameters);
517     }
518
519     isUsingExistingWF = (operation?: OperationModel): boolean => {
520         operation = operation || this.operation;
521         return operation.workflowAssociationType === WORKFLOW_ASSOCIATION_OPTIONS.EXISTING;
522     }
523
524     isUsingExternalWF = (operation?: OperationModel): boolean => {
525         operation = operation || this.operation;
526         return operation.workflowAssociationType === WORKFLOW_ASSOCIATION_OPTIONS.EXTERNAL;
527     }
528
529     shouldCreateWF = (operation?: OperationModel): boolean => {
530         operation = operation || this.operation;
531         return operation.workflowAssociationType === WORKFLOW_ASSOCIATION_OPTIONS.NEW;
532     }
533
534     checkFormValidForSubmit = (): boolean => {
535         return this.operation.name &&
536             (!this.isUsingExistingWF() || this.operation.workflowVersionId) &&
537             this.isParamsValid();
538     }
539
540     validityChanged = () => {
541         let validState = this.checkFormValidForSubmit();
542         this.validityChangedCallback(validState);
543     }
544
545     getSelectedDropdown(options: DropdownValue[], selectedValue: string): DropdownValue {
546         const selectedDropdown = _.find(options, (option) => option.value === selectedValue);
547         return selectedDropdown || this.toDropDownOption(null);
548     }
549
550     toDropDownOption(val: string) {
551         return { value : val, label: val };
552     }
553 }