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