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