1e217f10d428a454aebdb2f7b7a264ddd2a8aa55
[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         this.operation.workflowName = workflowId.label;
343         if (!this.assignInputParameters[this.operation.workflowName]) {
344             this.assignInputParameters[this.operation.workflowName] = {};
345             this.assignOutputParameters[this.operation.workflowName] = {};
346         }
347
348         this.isLoading = true;
349         this.validityChanged();
350         return this.workflowServiceNg2.getWorkflowVersions(this.operation.workflowId).subscribe((versions: Array<any>) => {
351             this.isLoading = false;
352
353             this.workflowVersions = _.map(
354                 _.filter(
355                     versions, version => version.state === this.workflowServiceNg2.VERSION_STATE_CERTIFIED
356                 ).sort((a, b) => a.name.localeCompare(b.name)),
357                 (version: any) => {
358                     if (!this.assignInputParameters[this.operation.workflowId][version.id] && version.id !== selectedVersionId) {
359                         this.assignInputParameters[this.operation.workflowId][version.id] = _.map(version.inputs, (input: any) => {
360                             return new OperationParameter({...input, type: input.type.toLowerCase(), required: Boolean(input.mandatory)});
361                         })
362                         .sort((a, b) => a.name.localeCompare(b.name));
363
364                         this.assignOutputParameters[this.operation.workflowId][version.id] = _.map(version.outputs, (output: any) => {
365                             return new OperationParameter({...output, type: output.type.toLowerCase(), required: Boolean(output.mandatory)});
366                         })
367                         .sort((a, b) => a.name.localeCompare(b.name));
368                     }
369                     return new DropdownValue(version.id, `V ${version.name}`);
370                 }
371             );
372             if (!selectedVersionId && this.workflowVersions.length) {
373                 this.operation.workflowVersionId = _.last(this.workflowVersions).value;
374                 this.operation.workflowVersion = _.last(this.workflowVersions).label;
375             }
376
377             this.changeWorkflowVersion(new DropDownOption(this.operation.workflowVersionId));
378             this.validityChanged();
379         });
380
381     }
382
383     changeWorkflowVersion(versionId: DropDownOption) {
384
385         if (_.isUndefined(versionId) || !this.workflowIsOnline) {
386             return;
387         }
388
389         this.operation.workflowVersionId = versionId.value;
390         this.inputParameters = this.assignInputParameters[this.operation.workflowId][this.operation.workflowVersionId];
391         this.outputParameters = this.assignOutputParameters[this.operation.workflowId][this.operation.workflowVersionId];
392         this.updateTable();
393         this.validityChanged();
394
395     }
396
397     toggleAssociateWorkflow(type: DropDownOption) {
398
399         if (_.isUndefined(type)) {
400             return;
401         }
402
403         this.operation.workflowAssociationType = type.value;
404         this.workflowAssociationType = this.operation.workflowAssociationType;
405
406         if (!this.isUsingExistingWF()) {
407             this.inputParameters = this.noAssignInputParameters;
408             this.outputParameters = this.noAssignOutputParameters;
409         } else {
410             if (!this.operation.workflowId || !this.operation.workflowVersionId) {
411                 this.inputParameters = [];
412                 this.outputParameters = [];
413             } else {
414                 this.inputParameters = this.assignInputParameters[this.operation.workflowId][this.operation.workflowVersionId];
415                 this.outputParameters = this.assignOutputParameters[this.operation.workflowId][this.operation.workflowVersionId];
416             }
417         }
418
419         this.updateTable();
420         this.validityChanged();
421
422     }
423
424     onChangeArtifactFile(e: any) {
425         const file = e.target.files && e.target.files[0];
426         this.operation.artifactFileName = file && file.name;
427
428         if (!this.operation.artifactFileName) {
429             this.operation.artifactData = null;
430             this.validityChanged();
431             return;
432         }
433
434         const reader = new FileReader();
435         reader.onloadend = () => {
436             this.isLoading = false;
437             const result = <String>reader.result;
438             this.operation.artifactData = result.substring(result.indexOf(',') + 1);
439             this.validityChanged();
440         }
441
442         this.isLoading = true;
443         reader.readAsDataURL(file);
444     }
445
446     tabChanged = (event) => {
447
448         this.currentTab = event.title;
449         this.updateTable();
450
451     }
452
453     updateTable() {
454
455         switch (this.currentTab) {
456             case this.TYPE_INPUT:
457                 this.tableParameters = this.inputParameters;
458                 break;
459             case this.TYPE_OUTPUT:
460                 this.tableParameters = this.outputParameters;
461                 break;
462         }
463
464     }
465
466     addParam(param?: OperationParameter): void {
467         this.tableParameters.push(new OperationParameter(param || {required: false}));
468         this.validityChanged();
469     }
470
471     canAdd = (): boolean => {
472
473         let valid = true;
474         if (this.currentTab === this.TYPE_INPUT) {
475             _.forEach(this.inputParameters, param => {
476                 if (!param.name || !param.inputId) {
477                     valid = false;
478                 }
479             });
480         } else {
481             _.forEach(this.outputParameters, param => {
482                 if (!param.name || !param.type) {
483                     valid = false;
484                 }
485             });
486         }
487
488         return valid;
489
490     }
491
492     isParamsValid = (): boolean => {
493
494         let valid = true;
495         _.forEach(this.inputParameters, param => {
496             if (!param.name || !param.inputId) {
497                 valid = false;
498             }
499         });
500         _.forEach(this.outputParameters, param => {
501             if (!param.name || !param.type) {
502                 valid = false;
503             }
504         });
505
506         return valid;
507
508     }
509
510     onRemoveParam = (param: OperationParameter): void => {
511         let index = _.indexOf(this.tableParameters, param);
512         this.tableParameters.splice(index, 1);
513         this.validityChanged();
514     }
515
516     createParamLists = () => {
517         this.operation.createInputsList(this.inputParameters);
518         this.operation.createOutputsList(this.outputParameters);
519     }
520
521     isUsingExistingWF = (operation?: OperationModel): boolean => {
522         operation = operation || this.operation;
523         return operation.workflowAssociationType === WORKFLOW_ASSOCIATION_OPTIONS.EXISTING;
524     }
525
526     isUsingExternalWF = (operation?: OperationModel): boolean => {
527         operation = operation || this.operation;
528         return operation.workflowAssociationType === WORKFLOW_ASSOCIATION_OPTIONS.EXTERNAL;
529     }
530
531     shouldCreateWF = (operation?: OperationModel): boolean => {
532         operation = operation || this.operation;
533         return operation.workflowAssociationType === WORKFLOW_ASSOCIATION_OPTIONS.NEW;
534     }
535
536     checkFormValidForSubmit = (): boolean => {
537         return this.operation.name &&
538             (!this.isUsingExistingWF() || this.operation.workflowVersionId) &&
539             this.isParamsValid();
540     }
541
542     validityChanged = () => {
543         let validState = this.checkFormValidForSubmit();
544         this.validityChangedCallback(validState);
545     }
546
547     getSelectedDropdown(options: DropdownValue[], selectedValue: string): DropdownValue {
548         const selectedDropdown = _.find(options, (option) => option.value === selectedValue);
549         return selectedDropdown || this.toDropDownOption(null);
550     }
551
552     toDropDownOption(val: string) {
553         return { value : val, label: val };
554     }
555 }