8c1279fbe6a752d95edcbb2c800b05917c7cf27f
[ccsdk/cds.git] /
1 import { Component, EventEmitter, OnDestroy, OnInit, Output, ViewChild } from '@angular/core';
2 import { FileSystemFileEntry, NgxFileDropEntry } from 'ngx-file-drop';
3 import { PackageCreationStore } from '../../package-creation.store';
4 import { TemplateInfo, TemplateStore } from '../../template.store';
5 import { Subject } from 'rxjs';
6 import { ResourceDictionary } from '../../mapping-models/ResourceDictionary.model';
7 import { DataTableDirective } from 'angular-datatables';
8 import { Mapping, MappingAdapter } from '../../mapping-models/mappingAdapter.model';
9 import { PackageCreationUtils } from '../../package-creation.utils';
10 import { JsonConvert, Any } from 'json2typescript';
11 import { ToastrService } from 'ngx-toastr';
12 import { SharedService } from '../shared-service';
13 import { XmlParser } from '../utils/ParserFactory/XmlParser';
14 import { TourService } from 'ngx-tour-md-menu';
15 import { PackageCreationService } from '../../package-creation.service';
16 import { ParserFactory } from '../utils/ParserFactory/ParserFactory';
17 import { TemplateType, FileExtension } from '../utils/TemplateType';
18 declare var $: any;
19
20 @Component({
21     selector: 'app-templ-mapp-creation',
22     templateUrl: './templ-mapp-creation.component.html',
23     styleUrls: ['./templ-mapp-creation.component.css']
24 })
25 export class TemplMappCreationComponent implements OnInit, OnDestroy {
26     @Output() showListView = new EventEmitter<any>();
27     @Output() showCreationView = new EventEmitter<any>();
28     public uploadedFiles: FileSystemFileEntry[] = [];
29     fileNames: Set<string> = new Set();
30     jsonConvert = new JsonConvert();
31     public files: NgxFileDropEntry[] = [];
32     fileName: any;
33     templateInfo = new TemplateInfo();
34     variables: string[] = [];
35     dtOptions: DataTables.Settings = {};
36     initDtOptions: DataTables.Settings = {};
37     // We use this trigger because fetching the list of persons can be quite long,
38     // thus we ensure the data is fetched before rendering
39     dtTrigger = new Subject();
40     resTableDtTrigger = new Subject();
41     resourceDictionaryRes: ResourceDictionary[] = [];
42     allowedExt = ['.vtl'];
43     @ViewChild(DataTableDirective, { static: false })
44     dtElement: DataTableDirective;
45     MappingAdapter: MappingAdapter;
46     mapping = new Map();
47     templateFileContent: string;
48     templateExt = 'vtl';
49     dependancies = new Map<string, Array<string>>();
50     dependanciesSource = new Map<string, string>();
51     mappingRes = [];
52     currentTemplate: any;
53     currentMapping: any;
54     edit = false;
55     templatesExist = false;
56     fileToDelete: any = {};
57     parserFactory: ParserFactory;
58     selectedProps: Set<string>;
59
60     constructor(
61         private packageCreationStore: PackageCreationStore,
62         private templateStore: TemplateStore,
63         private packageCreationUtils: PackageCreationUtils,
64         private toastr: ToastrService,
65         private sharedService: SharedService,
66         private packageCreationService: PackageCreationService,
67         private tourService: TourService,
68     ) {
69     }
70
71     ngOnInit() {
72         this.selectedProps = new Set<string>();
73         this.parserFactory = new ParserFactory();
74         this.templateStore.state$.subscribe(templateInfo => {
75             // init Template&mapping vars
76             console.log('Oninit');
77             console.log(templateInfo);
78             this.templateInfo = templateInfo;
79             this.fileToDelete = templateInfo.fileName;
80             this.fileName = templateInfo.fileName.split('/')[1];
81             if (this.fileName) {
82                 this.fileName = this.fileName.split('-')[0];
83             }
84             if (templateInfo.type === 'mapping' || templateInfo.type.includes('mapping')) {
85                 this.mappingRes = templateInfo.mapping;
86                 this.currentMapping = Object.assign({}, templateInfo);
87                 this.resourceDictionaryRes = [];
88                 this.resTableDtTrigger.next();
89             } else {
90                 this.mappingRes = [];
91                 this.currentMapping = Any;
92                 this.resourceDictionaryRes = [];
93             }
94             this.templateFileContent = templateInfo.fileContent;
95             this.templateExt = this.templateInfo.ext || this.templateExt;
96             this.currentTemplate = Object.assign({}, templateInfo);
97
98             if (templateInfo.type === 'template' || templateInfo.type.includes('template')) {
99                 console.log('template extension ' + this.templateExt);
100                 this.currentTemplate.fileName = 'Templates/' + this.fileName + '-template.' + this.templateExt;
101                 console.log(this.currentTemplate.fileName);
102             } else {
103                 this.currentTemplate = Any;
104             }
105
106         });
107
108
109         this.sharedService.isEdit().subscribe(res => {
110             console.log('------------------------....');
111             this.templatesExist = this.packageCreationStore.state.templates.files.size > 0
112                 || this.packageCreationStore.state.mapping.files.size > 0;
113             console.log(res);
114             this.edit = res;
115
116             if (!this.edit) {
117                 console.log('remove ----');
118                 this.currentMapping = {};
119                 this.currentTemplate = {};
120                 this.fileName = '';
121                 this.templateFileContent = '';
122                 this.resourceDictionaryRes = [];
123                 this.mappingRes = [];
124             }
125         });
126
127         this.initDtOptions = {
128             pagingType: 'full_numbers',
129             pageLength: 25,
130             destroy: true,
131             retrieve: true,
132             columnDefs: [
133                 {
134                     targets: [0, 1, 2], // column or columns numbers
135                     orderable: false, // set orderable for selected columns
136                     searchable: false,
137                 },
138
139             ],
140         };
141         this.dtOptions = {
142             pagingType: 'full_numbers',
143             pageLength: 25,
144             destroy: true,
145             retrieve: true,
146         };
147     }
148
149     setProp(e, propName, index) {
150         this.resourceDictionaryRes[index][propName] = e.checked;
151         console.log(this.resourceDictionaryRes[index]);
152     }
153     selectProp(value) {
154         console.log(value);
155         if (this.selectedProps.has(value)) {
156             this.selectedProps.delete(value);
157         } else {
158             this.selectedProps.add(value);
159         }
160     }
161
162     removeProps() {
163         console.log(this.selectedProps);
164         this.selectedProps.forEach(prop => {
165             this.resourceDictionaryRes.forEach((res, index) => {
166                 if (res.name === prop) {
167                     console.log('delete...');
168                     this.resourceDictionaryRes.splice(index, 1);
169                     this.selectedProps.delete(prop);
170                 }
171             });
172         });
173     }
174     selectAllProps() {
175         if (this.resourceDictionaryRes.length === this.selectedProps.size) {
176             this.selectedProps = new Set<string>();
177         } else {
178             this.resourceDictionaryRes.forEach(prop => {
179                 console.log(prop);
180                 this.selectedProps.add(prop.name);
181             });
182         }
183
184     }
185     reMap() {
186         let currentResDictionary = [];
187         if (this.selectedProps && this.selectedProps.size > 0) {
188             console.log('base');
189             this.packageCreationService.getTemplateAndMapping([...this.selectedProps]).subscribe(res => {
190                 let message = 'Re-Auto mapping';
191                 this.mappingRes = [];
192                 currentResDictionary = res;
193                 console.log(currentResDictionary);
194                 if (currentResDictionary && currentResDictionary.length <= 0) {
195                     message = 'No values for those attributes';
196                 }
197
198                 // Replcae new values with the old ones
199                 currentResDictionary.forEach(curr => {
200                     for (let i = 0; i < this.resourceDictionaryRes.length; i++) {
201                         if (this.resourceDictionaryRes[i].name === curr.name) {
202                             this.resourceDictionaryRes[i] = curr;
203                         }
204                     }
205                 });
206                 this.rerender();
207                 this.toastr.success(message, 'Success');
208             }, err => {
209                 this.toastr.error('Error');
210             });
211         }
212
213     }
214
215     getFileExtension() {
216         switch (this.templateExt) {
217             case 'vtl':
218                 return '.vtl';
219             case 'kt':
220                 return '.ktl';
221             case 'j2':
222                 return '.j2';
223             default:
224                 return '.vtl';
225         }
226     }
227
228     fileExtensionFromString(filename: string): string {
229         const fileExtension = filename.substring(filename.lastIndexOf('.') + 1);
230         return fileExtension;
231     }
232
233     public getTemplateVariable(fileContent: string) {
234         // TODO: implement factory Pattern for parser
235         console.log('start parsing........ ' + this.templateExt);
236         const parser = this.parserFactory.getParser(fileContent, this.templateExt);
237         return parser.getVariables(fileContent);
238     }
239
240     public dropped(files: NgxFileDropEntry[]) {
241         this.files = files;
242         for (const droppedFile of files) {
243             // Is it a file? & Not added before
244             if (droppedFile.fileEntry.isFile) {
245                 const fileEntry = droppedFile.fileEntry as FileSystemFileEntry;
246                 this.uploadedFiles.push(fileEntry);
247                 this.fileNames.add(fileEntry.name);
248
249             }
250         }
251     }
252     removeFile(index) {
253         this.uploadedFiles.splice(index, 1);
254     }
255
256     confirmDelete() {
257         // Delete from templates
258         this.sharedService.deleteFromList(this.fileName);
259         this.packageCreationStore.state.templates.files.delete(this.fileToDelete);
260         // Delete from Mapping
261         this.packageCreationStore.state.mapping.files.delete(this.fileToDelete);
262         if (
263             this.packageCreationStore.state.templates.files.size > 0 ||
264             this.packageCreationStore.state.mapping.files.size > 0
265         ) {
266             this.openListView();
267         }
268
269     }
270     uploadFile() {
271         this.dependancies.clear();
272         this.dependanciesSource.clear();
273         if (this.allowedExt.includes('.csv') || this.allowedExt.includes('.xml')) {
274             this.fetchkeysfromfile();
275         } else {
276             this.setTemplateFilesToStore();
277         }
278         $('.btn-cancel').click();
279     }
280
281     fetchkeysfromfile() {
282         for (const droppedFile of this.uploadedFiles) {
283             droppedFile.file((file: File) => {
284                 const fileReader = new FileReader();
285                 fileReader.onload = (e) => {
286                     const fileExt = this.fileExtensionFromString(droppedFile.name);
287                     if (fileExt === 'csv') {
288                         this.variables = fileReader.result.toString().split(',');
289                     } else {
290                         const parser = new XmlParser();
291                         this.variables = parser.getVariables(fileReader.result.toString());
292                     }
293                     console.log('variables = ' + this.variables);
294                     this.getMappingTableFromTemplate(null);
295
296                 };
297                 fileReader.readAsText(file);
298             });
299         }
300         this.uploadedFiles = [];
301     }
302
303     private convertDictionaryToMap(resourceDictionaries: ResourceDictionary[]): Mapping[] {
304         const mapArray: Mapping[] = [];
305         for (const resourceDictionary of resourceDictionaries) {
306             this.MappingAdapter = new MappingAdapter(resourceDictionary, this.dependancies, this.dependanciesSource);
307             mapArray.push(this.MappingAdapter.ToMapping());
308         }
309         console.log(mapArray);
310         return mapArray;
311     }
312
313     setTemplateFilesToStore() {
314         for (const droppedFile of this.uploadedFiles) {
315             droppedFile.file((file: File) => {
316                 const fileReader = new FileReader();
317                 fileReader.onload = (e) => {
318                     this.templateFileContent = fileReader.result.toString();
319                     // this.variables = this.getTemplateVariable(this.templateFileContent);
320                     // console.log(this.variables);
321
322                 };
323                 fileReader.readAsText(file);
324             });
325         }
326         this.uploadedFiles = [];
327     }
328
329     textChanges(code: any, fileName: string) {
330         //  this.packageCreationStore.addTemplate(fileName, code);
331         this.templateFileContent = code;
332     }
333
334     public fileOver(event) {
335         console.log(event);
336     }
337
338     public fileLeave(event) {
339         console.log(event);
340     }
341     //
342     resetTheUploadedFiles() {
343         this.uploadedFiles = [];
344     }
345
346     openListView() {
347         console.log('open List view');
348         this.showListView.emit('tell parent to open create views');
349     }
350
351     openCreationView() {
352         console.log('close creation view');
353         this.showCreationView.emit('close create form and open list');
354     }
355
356     identify(index, item) {
357         return item.name;
358     }
359     setVelocity(index, value) {
360         // console.log('velocity value = ' + value);
361         // console.log(this.resourceDictionaryRes[index]);
362         // tslint:disable-next-line: no-string-literal
363         this.resourceDictionaryRes[index].definition.property['metadata'] = {
364             'transform-template': value
365         };
366         console.log(this.resourceDictionaryRes[index]);
367     }
368
369     getMappingTableFromTemplate(e) {
370         console.log('-' + this.templateFileContent + '-');
371         this.resourceDictionaryRes = [];
372         if (e) {
373             e.preventDefault();
374         }
375         this.variables = this.getTemplateVariable(this.templateFileContent);
376         console.log('variables = ' + this.variables);
377         if (this.variables && this.variables.length > 0) {
378             console.log('base');
379             this.packageCreationService.getTemplateAndMapping(this.variables).subscribe(res => {
380                 let message = 'Attributes are Fetched';
381                 this.mappingRes = [];
382                 this.resourceDictionaryRes = res;
383                 console.log(this.resourceDictionaryRes);
384                 this.rerender();
385                 if (this.resourceDictionaryRes && this.resourceDictionaryRes.length <= 0) {
386                     message = 'No values for those attributes';
387                 }
388                 this.toastr.success(message, 'Success');
389             }, err => {
390                 this.toastr.error('Error');
391             });
392         } else {
393             this.toastr.error('Empty or Invalid file format. Validate your file first');
394         }
395     }
396
397     initMap(key, map) {
398         if (!this.dependanciesSource.has(key)) {
399             this.dependanciesSource.set(key, map.key);
400         }
401         return map.key;
402     }
403     clear() {
404         this.fileName = '';
405         this.templateFileContent = '';
406         this.resourceDictionaryRes = [];
407         this.mappingRes = [];
408         this.currentMapping = {};
409         this.currentTemplate = {};
410         //   this.closeCreationForm();
411     }
412     cancel() {
413         this.openListView();
414     }
415     saveToStore() {
416         if (this.fileName) {
417             // check file duplication
418             console.log('----------- mode ' + this.edit);
419             if (
420                 (!(this.packageCreationStore.fileExist('Templates/' + this.fileName + '-mapping.json')
421                     || this.packageCreationStore.fileExist('Templates/' + this.fileName + '-template' + this.getFileExtension())))
422                 || this.edit
423             ) {
424                 // Save Mapping to Store
425                 if (this.resourceDictionaryRes && this.resourceDictionaryRes.length > 0) {
426                     const mapArray = this.convertDictionaryToMap(this.resourceDictionaryRes);
427                     this.packageCreationStore.addMapping('Templates/' + this.fileName + '-mapping.json',
428                         this.packageCreationUtils.transformToJson(this.jsonConvert.serialize(mapArray)));
429                     this.resourceDictionaryRes = [];
430                 }
431                 // Save Template to store
432                 // if (this.templateFileContent) {
433                 this.packageCreationStore.addTemplate('Templates/' + this.fileName + '-template' + this.getFileExtension(),
434                     this.templateFileContent);
435                 this.templateFileContent = '';
436                 //  }
437                 this.fileName = '';
438                 this.toastr.success('File is created', 'success');
439                 this.openListView();
440                 if (localStorage.getItem('tour-guide') !== 'end' && localStorage.getItem('tour-guide') !== 'false') {
441                     this.tourService.goto('tm-templateEdit');
442                 }
443             } else {
444                 console.log('this file already exist');
445                 this.toastr.error('File name already exist', 'Error');
446             }
447         } else {
448             this.toastr.error('Add the file name', 'Error');
449         }
450     }
451
452
453     selectSource(dict, e) {
454         const source = e.target.value;
455         let keyDepend = null;
456         this.dependancies.set(dict.name, null);
457         try {
458             keyDepend = dict.definition.sources[source].properties['key-dependencies'] || null;
459         } catch (e) { }
460         console.log(dict);
461         console.log(source);
462         if (keyDepend) {
463             this.dependancies.set(dict.name, keyDepend);
464         } else {
465             // this.dependancies.delete(dict.name);
466             // this.dependanciesSource.delete(dict.name);
467         }
468         this.dependanciesSource.set(dict.name, source);
469         console.log(this.dependancies);
470         console.log(this.dependanciesSource);
471     }
472
473     getKeys(map: Map<string, any>) {
474         return Array.from(map.keys());
475     }
476
477     getValue(key) {
478         return this.dependancies.get(key);
479     }
480
481     rerender(): void {
482         this.dtTrigger.next();
483     }
484
485     ngOnDestroy(): void {
486         // Do not forget to unsubscribe the event
487         this.dtTrigger.unsubscribe();
488         this.resTableDtTrigger.unsubscribe();
489         // this.templateStore.unsubscribe();
490     }
491 }
492
493 class DependancyVal {
494     source: string;
495     keyDepend: any;
496     constructor(
497         source: string,
498         keyDepend: any
499     ) {
500         this.source = source;
501         this.keyDepend = keyDepend;
502     }
503 }