Merge "CDS API reference add request classes"
[ccsdk/cds.git] / cds-ui / designer-client / src / app / modules / feature-modules / packages / designer / functions-attribute / functions-attribute.component.ts
1 import { Component, EventEmitter, OnDestroy, OnInit, Output } from '@angular/core';
2 import { DesignerStore } from '../designer.store';
3 import { PackageCreationStore } from '../../package-creation/package-creation.store';
4 import { Subject } from 'rxjs';
5 import { distinctUntilChanged, takeUntil } from 'rxjs/operators';
6 import { CBAPackage } from '../../package-creation/mapping-models/CBAPacakge.model';
7 import { TemplateAndMapping } from '../../package-creation/template-mapping/TemplateAndMapping';
8 import { FunctionsStore } from '../functions.store';
9 import { NodeProcess, NodeTemplate } from '../model/desinger.nodeTemplate.model';
10 import { DesignerDashboardState } from '../model/designer.dashboard.state';
11 import { Action } from '../action-attributes/models/Action';
12
13 @Component({
14     selector: 'app-functions-attribute',
15     templateUrl: './functions-attribute.component.html',
16     styleUrls: ['./functions-attribute.component.css']
17 })
18 export class FunctionsAttributeComponent implements OnInit, OnDestroy {
19
20     ngUnsubscribe = new Subject();
21     designerDashboardState: DecodeSuccessCallback;
22     cbaPackage: CBAPackage;
23     templateAndMappingMap = new Map<string, TemplateAndMapping>();
24     selectedTemplates = new Map<string, TemplateAndMapping>();
25     fileToDelete: string;
26     requiredInputs = new Map<string, {}>();
27     requiredOutputs = new Map<string, {}>();
28     OptionalInputs = new Map<string, {}>();
29     optionalOutputs = new Map<string, {}>();
30     artifactPrefix = false;
31     currentFuncion = new NodeProcess();
32     nodeTemplates = new NodeTemplate('');
33     designerState: DesignerDashboardState;
34     actionName = '';
35     functionName = '';
36     interfaceChildName = '';
37     @Output() saveEvent = new EventEmitter<string>();
38
39
40     constructor(
41         private designerStore: DesignerStore,
42         private packageCreationStore: PackageCreationStore,
43         private functionStore: FunctionsStore
44     ) {
45     }
46
47     ngOnInit() {
48         this.designerStore.state$.subscribe(designerDashboardState => {
49             this.designerState = designerDashboardState;
50             this.actionName = this.designerState.actionName;
51             const action = this.designerState.template.workflows[this.actionName] as Action;
52             this.currentFuncion = new NodeProcess();
53             try {
54                 console.log(action);
55                 if (action) {
56                     // this.designerState.functionName
57                     const child = Object.keys(action.steps)[0];
58                     this.functionName = this.designerState.functionName;
59                     console.log(this.designerState.template.node_templates);
60                     console.log(this.designerState);
61                     console.log(this.designerState.template.node_templates[this.functionName]);
62                     //  this.currentFuncion = this.designerState.template.node_templates[this.functionName];
63                     // reset inouts&outputs
64                     this.requiredInputs = new Map<string, {}>();
65                     this.requiredOutputs = new Map<string, {}>();
66                     this.OptionalInputs = new Map<string, {}>();
67                     this.optionalOutputs = new Map<string, {}>();
68                     this.toNodeProcess(this.designerState.template.node_templates[this.functionName], this.functionName);
69                     const type = this.designerState.template.node_templates[this.functionName].type;
70                     this.getNodeType(type);
71                     this.onInitMapping();
72                 }
73             } catch (e) { }
74         });
75
76         this.packageCreationStore.state$
77             .subscribe(cbaPackage => {
78                 this.cbaPackage = cbaPackage;
79                 console.log('File name =>================== ');
80                 console.log(this.cbaPackage.templates.files);
81                 this.cbaPackage.templates.files.forEach((value, key) => {
82                     console.log('File name => ' + key);
83                     const templateAndMapping = new TemplateAndMapping();
84                     templateAndMapping.isTemplate = true;
85                     const isFromTemplate = true;
86                     this.setIsMappingOrTemplate(key, templateAndMapping, isFromTemplate);
87                 });
88
89                 this.cbaPackage.mapping.files.forEach((value, key) => {
90                     const templateAndMapping = new TemplateAndMapping();
91                     templateAndMapping.isMapping = true;
92                     const isFromTemplate = false;
93                     this.setIsMappingOrTemplate(key, templateAndMapping, isFromTemplate);
94                 });
95             });
96
97     }
98
99     onInitMapping() {
100         // selectedTemplates , templateAndMappingMap
101         this.selectedTemplates = new Map<string, TemplateAndMapping>();
102         try {
103             const functionMap = this.designerState.template.node_templates[this.functionName].artifacts;
104             console.log(this.templateAndMappingMap);
105
106             Object.keys(functionMap).forEach((file) => {
107                 const filename = file.substring(0, file.lastIndexOf('-'));
108                 console.log(filename);
109                 if (this.templateAndMappingMap.has(filename)) {
110                     this.selectedTemplates.set(filename, this.templateAndMappingMap.get(filename));
111                 }
112             });
113
114
115         } catch (e) { }
116     }
117
118     toNodeProcess(nodeTemplate, functionName) {
119         console.log(nodeTemplate);
120         this.currentFuncion['instance-name'] = functionName;
121         // tslint:disable-next-line: no-string-literal
122         this.currentFuncion['type'] = nodeTemplate['type'];
123         if (nodeTemplate.interfaces && Object.keys(nodeTemplate.interfaces).length > 0) {
124             const nodeName = Object.keys(nodeTemplate.interfaces)[0];
125             // console.log(Object.keys(nodeTemplate.interfaces));
126             // tslint:disable-next-line: no-string-literal
127             const inputs = nodeTemplate.interfaces[nodeName]['operations']['process']['inputs'];
128             // tslint:disable-next-line: no-string-literal
129             const outputs = nodeTemplate.interfaces[nodeName]['operations']['process']['outputs'];
130
131             // console.log(inputs);
132
133             if (inputs) {
134                 for (const [key, value] of Object.entries(inputs)) {
135                     console.log(key + ' - ' + value);
136                     if (typeof value === 'object') {
137                         this.currentFuncion.inputs[key] = JSON.stringify(value);
138                     } else {
139                         this.currentFuncion.inputs[key] = value;
140                     }
141                 }
142             }
143             if (outputs) {
144                 for (const [key, value] of Object.entries(outputs)) {
145                     console.log(key + '-' + value);
146                     this.currentFuncion.outputs[key] = value;
147                 }
148             }
149         }
150     }
151     ngOnDestroy() {
152         this.ngUnsubscribe.next();
153         this.ngUnsubscribe.complete();
154     }
155
156     addTemplates() { }
157
158     saveFunctionData() {
159         this.nodeTemplates = new NodeTemplate('');
160         // tslint:disable-next-line: variable-name
161         const node_templates = {};
162         const finalFunctionData = this.currentFuncion;
163         // tslint:disable-next-line: no-string-literal
164         const type = finalFunctionData['type'];
165         const instanceName = finalFunctionData['instance-name'];
166         // insert selected templates in nodeTemplates.artifacts
167         this.selectedTemplates.forEach((value, key) => {
168             console.log(key);
169             console.log(value);
170             console.log(finalFunctionData.inputs['artifact-prefix-names']);
171
172             if (finalFunctionData.inputs['artifact-prefix-names'] === undefined) {
173                 finalFunctionData.inputs['artifact-prefix-names'] = [key];
174             } else if (
175                 Array.isArray(finalFunctionData.inputs['artifact-prefix-names']) &&
176                 !finalFunctionData.inputs['artifact-prefix-names'].includes(key)
177             ) {
178                 finalFunctionData.inputs['artifact-prefix-names'].push(key);
179             }
180
181             if (value.isMapping) {
182                 this.nodeTemplates.artifacts[key + '-mapping'] = {
183                     type: 'artifact-mapping-resource',
184                     file: 'Templates/' + key + '-mapping.json'
185                 };
186             }
187
188             if (value.isTemplate) {
189                 this.nodeTemplates.artifacts[key + '-template'] = {
190                     type: 'artifact-template-velocity',
191                     file: 'Templates/' + key + '-template.vtl'
192                 };
193             }
194         });
195         // instantiate the final node_template object to save
196
197         this.nodeTemplates.type = type;
198         delete this.nodeTemplates.properties;
199         node_templates[finalFunctionData['instance-name']] = this.nodeTemplates;
200
201         delete finalFunctionData['instance-name'];
202         // tslint:disable-next-line: no-string-literal
203         delete finalFunctionData['type'];
204
205         if (finalFunctionData.outputs === {} || Object.keys(finalFunctionData.outputs).length <= 0) {
206             delete finalFunctionData.outputs;
207         }
208
209         this.nodeTemplates.interfaces = {
210             [this.interfaceChildName]: {
211                 operations: {
212                     process: {
213                         ...finalFunctionData,
214                     }
215                 }
216             }
217         };
218         console.log(finalFunctionData);
219         console.log(node_templates);
220         // save function to store
221         // tslint:disable-next-line: no-unused-expression
222         this.designerStore.addNodeTemplate(instanceName, type, node_templates[instanceName]);
223         // create a new package
224         this.saveEvent.emit('save');
225     }
226     // Template logic
227     private setIsMappingOrTemplate(key: string, templateAndMapping: TemplateAndMapping, isFromTemplate: boolean) {
228         const nameOfFile = isFromTemplate ?
229             key.split('/')[1].split('.')[0].split('-template')[0]
230             : key.split('/')[1].split('.')[0].split('-mapping')[0];
231         // const fullName = nameOfFile + ',' + key.split('.');
232         if (this.templateAndMappingMap.has(nameOfFile)) {
233             const templateAndMappingExisted = this.templateAndMappingMap.get(nameOfFile);
234             !isFromTemplate ? templateAndMappingExisted.isMapping = true : templateAndMappingExisted.isTemplate = true;
235             this.templateAndMappingMap.set(nameOfFile, templateAndMappingExisted);
236         } else {
237             this.templateAndMappingMap.set(nameOfFile, templateAndMapping);
238         }
239
240     }
241
242     setArtifact(predefined: boolean) {
243         if (predefined) {
244             this.currentFuncion.inputs['artifact-prefix-names'] = [];
245         } else {
246             this.currentFuncion.inputs['artifact-prefix-names'] = { get_input: 'template-prefix' };
247         }
248     }
249     addToInputs(optionalInput) {
250         this.requiredInputs.set(optionalInput, this.OptionalInputs.get(optionalInput));
251         this.OptionalInputs.delete(optionalInput);
252     }
253
254     setTemplate(file: string) {
255         if (this.selectedTemplates.has(file)) {
256             this.selectedTemplates.delete(file);
257         } else {
258             this.selectedTemplates.set(file, this.templateAndMappingMap.get(file));
259         }
260         console.log(this.selectedTemplates);
261     }
262
263     getKeys(map: Map<string, any>) {
264         return Array.from(map.keys());
265     }
266     getValue(file: string, map: Map<string, any>) {
267         return map.get(file);
268     }
269
270     getObjectKey(object) {
271         // console.log(object);
272         return Object.keys(object);
273     }
274     getObjectValue(object) {
275         return Object.values(object);
276     }
277
278     getNodeType(nodeName: string) {
279         this.functionStore.state$
280             .subscribe(state => {
281                 console.log(state);
282                 console.log(nodeName);
283                 const functions = state.serverFunctions;
284                 // tslint:disable-next-line: prefer-for-of
285                 for (let i = 0; i < functions.length; i++) {
286                     if (functions[i].modelName === nodeName) {
287                         // tslint:disable: no-string-literal
288                         console.log(functions[i].definition['interfaces']);
289                         this.getInputFields(functions[i].definition['interfaces'], 'outputs');
290                         this.getInputFields(functions[i].definition['interfaces'], 'inputs');
291                         break;
292                     }
293                 }
294             });
295     }
296
297     getInputFields(interfaces, type) {
298
299         if (type === 'inputs') {
300             this.requiredInputs = new Map<string, {}>();
301             this.OptionalInputs = new Map<string, {}>();
302         } else {
303             this.requiredOutputs = new Map<string, {}>();
304             this.optionalOutputs = new Map<string, {}>();
305
306         }
307         const nodeName = Object.keys(interfaces)[0];
308         this.interfaceChildName = nodeName;
309         console.log(nodeName + ' ------ ' + type);
310         console.log(interfaces[nodeName]['operations']['process'][type]);
311         const fields = interfaces[nodeName]['operations']['process'][type];
312         this.artifactPrefix = false;
313         for (const [key, value] of Object.entries(fields)) {
314             if (key === 'artifact-prefix-names') {
315                 this.artifactPrefix = true;
316                 // in edit&view mode need to check first if this input||output already exists
317             } else if (key in this.currentFuncion.inputs) {
318                 this.requiredInputs.set(key, Object.assign({}, value));
319             } else if (key in this.currentFuncion.outputs) {
320                 this.requiredOutputs.set(key, Object.assign({}, value));
321             } else if (value['required']) {
322                 console.log('This field is required = ' + key);
323                 if (type === 'inputs') {
324                     this.requiredInputs.set(key, Object.assign({}, value));
325                 } else {
326                     this.requiredOutputs.set(key, Object.assign({}, value));
327                 }
328             } else {
329                 console.log('This field is Optional ' + key);
330                 if (type === 'inputs') {
331                     this.OptionalInputs.set(key, Object.assign({}, value));
332                 } else {
333                     this.optionalOutputs.set(key, Object.assign({}, value));
334                 }
335             }
336         }
337
338         // console.log(this.requiredOutputs);
339     }
340
341
342 }