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