Improve Rest Service API
[ccsdk/cds.git] / cds-ui / client / src / app / feature-modules / blueprint / modify-template / editor / editor.component.ts
1 /*
2 ============LICENSE_START==========================================
3 ===================================================================
4 Copyright (C) 2018-19 IBM Intellectual Property. All rights reserved.
5 ===================================================================
6
7 Unless otherwise specified, all software contained herein is licensed
8 under the Apache License, Version 2.0 (the License);
9 you may not use this software except in compliance with the License.
10 You may obtain a copy of the License at
11
12     http://www.apache.org/licenses/LICENSE-2.0
13
14 Unless required by applicable law or agreed to in writing, software
15 distributed under the License is distributed on an "AS IS" BASIS,
16 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17 See the License for the specific language governing permissions and
18 limitations under the License.
19 ============LICENSE_END============================================
20 */
21
22 import { Component, OnInit, ViewChild } from '@angular/core';
23 import { FlatTreeControl } from '@angular/cdk/tree';
24 import { MatTreeFlatDataSource, MatTreeFlattener } from '@angular/material/tree';
25 import { IBlueprint } from 'src/app/common/core/store/models/blueprint.model';
26 import "ace-builds/webpack-resolver";
27 import 'brace';
28 import 'brace/ext/language_tools';
29 import 'ace-builds/src-min-noconflict/snippets/html';
30 import * as JSZip from 'jszip';
31 import { saveAs } from 'file-saver';
32
33 import { IAppState } from '../../../../common/core/store/state/app.state';
34 import { Store } from '@ngrx/store';
35 import { Observable } from 'rxjs';
36 import { IBlueprintState } from 'src/app/common/core/store/models/blueprintState.model';
37 import { LoadBlueprintSuccess, SetBlueprintState } from '../../../../common/core/store/actions/blueprint.action'
38
39
40 interface Node {
41   name: string;
42   children?: Node[];
43   data?: any
44 }
45
46 const TREE_DATA: Node[] = [
47   {
48     name: 'Definitions',
49     children: [
50       { name: 'activation-blueprint.json' },
51       { name: 'artifacts_types.json' },
52       { name: 'data_types.json' },
53     ]
54   }
55 ];
56
57 /** Flat node with expandable and level information */
58 interface ExampleFlatNode {
59   expandable: boolean;
60   name: string;
61   level: number;
62 }
63
64
65 @Component({
66   selector: 'app-editor',
67   templateUrl: './editor.component.html',
68   styleUrls: ['./editor.component.scss']
69 })
70 export class EditorComponent implements OnInit {
71
72   @ViewChild('editor') editor;
73   blueprintdata: IBlueprint;
74   blueprint: IBlueprint;
75   bpState: Observable<IBlueprintState>;
76   text: string;
77   filesTree: any = [];
78   filesData: any = [];
79   selectedFile: string;
80   zipFolder: any;
81   blueprintName: string;
82   fileExtension: string;
83   mode: string;
84   private zipFile: JSZip = new JSZip();
85   activeNode: any;
86   selectedFolder: string;
87   activationBlueprint: string;
88   isNameTextboxEnablled : boolean = false;
89   fileAction : string;
90   filetoDelete : string;
91
92   private transformer = (node: Node, level: number) => {
93     return {
94       expandable: !!node.children && node.children.length > 0,
95       name: node.name,
96       level: level,
97     };
98   }
99
100   treeControl = new FlatTreeControl<ExampleFlatNode>(
101     node => node.level, node => node.expandable);
102
103   treeFlattener = new MatTreeFlattener(
104     this.transformer, node => node.level, node => node.expandable, node => node.children);
105
106   dataSource = new MatTreeFlatDataSource(this.treeControl, this.treeFlattener);
107
108   constructor(private store: Store<IAppState>) {
109     this.dataSource.data = TREE_DATA;
110     this.bpState = this.store.select('blueprint');
111     // this.dataSource.data = TREE_DATA;
112   }
113
114   hasChild = (_: number, node: ExampleFlatNode) => node.expandable;
115
116   ngOnInit() {
117     this.editorContent();
118     this.dataSource.data = this.filesTree;
119   }
120
121   fileClicked(file) {
122     console.log('selected file:' + file);
123   }
124   editorContent() {
125     this.editor.setTheme("eclipse");
126     this.editor.getEditor().setOptions({
127       // enableBasicAutocompletion: true,
128       fontSize: "100%",
129       printMargin: false,
130     });
131     this.editor.getEditor().commands.addCommand({
132       name: "showOtherCompletions",
133       bindKey: "Ctrl-.",
134       exec: function (editor) {
135
136       }
137     })
138     this.bpState.subscribe(
139       blueprintdata => {
140         var blueprintState: IBlueprintState = { blueprint: blueprintdata.blueprint, isLoadSuccess: blueprintdata.isLoadSuccess, isSaveSuccess: blueprintdata.isSaveSuccess, isUpdateSuccess: blueprintdata.isUpdateSuccess };
141         this.blueprintdata = blueprintState.blueprint;
142         this.filesTree = blueprintdata.files;
143         this.filesData = blueprintdata.filesData;
144         this.dataSource.data = this.filesTree;
145         this.blueprintName = blueprintdata.name;
146         let blueprint = [];
147         for (let key in this.blueprintdata) {
148           if (this.blueprintdata.hasOwnProperty(key)) {
149             blueprint.push(this.blueprintdata[key]);
150           }
151         }
152         // this.text = JSON.stringify(this.blueprintdata, null, '\t');
153         // this.editor.getEditor().getSession().setMode("ace/mode/json");
154         this.editor.getEditor().getSession().setTabSize(2);
155         this.editor.getEditor().getSession().setUseWrapMode(true);
156         this.setEditorMode();
157       })
158   }
159
160   updateBlueprint() {
161     console.log(this.blueprint);
162     this.filesData.forEach(fileNode => {
163       if (this.selectedFile && fileNode.name.includes(this.blueprintName.trim()) && fileNode.name.includes(this.selectedFile.trim())) {
164         fileNode.data = this.text;
165       } else if (this.selectedFile && fileNode.name.includes(this.selectedFile.trim())) {
166         fileNode.data = this.text;
167       }
168     });
169
170     if (this.selectedFile && this.selectedFile == this.blueprintName.trim()) {
171       this.blueprint = JSON.parse(this.text);
172     } else {
173       this.blueprint = this.blueprintdata;
174     }
175
176     let blueprintState = {
177       blueprint: this.blueprint,
178       name: this.blueprintName,
179       files: this.filesTree,
180       filesData: this.filesData
181     }
182     this.store.dispatch(new SetBlueprintState(blueprintState));
183     // console.log(this.text);
184   }
185
186   selectFileToView(file) {
187     this.selectedFile = file.name;
188     this.filetoDelete = file.name;
189     this.filesData.forEach((fileNode) => {
190       if (fileNode.name.includes(file.name)) {
191         this.text = fileNode.data;
192       }
193     })
194     this.fileExtension = this.selectedFile.substr(this.selectedFile.lastIndexOf('.') + 1);
195     // console.log(this.fileExtension);
196     this.setEditorMode();
197   }
198
199   SaveToBackend() {
200     this.zipFile.generateAsync({ type: "blob" })
201       .then(blob => {
202
203       });
204   }
205
206   deploy() {
207     // to do
208   }
209
210   create() {
211     this.filesData.forEach((path) => {
212       this.zipFile.file(path.name, path.data);
213     });
214   }
215
216   download() {
217     this.create();
218     var zipFilename = "baseconfiguration.zip";
219     this.zipFile.generateAsync({ type: "blob" })
220       .then(blob => {
221         saveAs(blob, zipFilename);
222       });
223   }
224   setEditorMode() {
225     switch (this.fileExtension) {
226       case "xml":
227         this.mode = 'xml';
228         break;
229       case "py":
230         this.mode = 'python';
231         break;
232       case "kts":
233         this.mode = 'kotlin';
234         break;
235       case "txt":
236         this.mode = 'text';
237         break;
238       case "meta":
239         this.mode = 'text';
240         break;
241       case "vtl":
242         this.mode = 'velocity';
243         break;
244       default:
245         this.mode = 'json';
246     }
247   }
248
249   selectFolder(node) {
250     this.selectedFolder = node.name;
251     this.filetoDelete = node.name;
252     console.log(node);
253     // this.createFolderOrFile(node.name, 'folder');
254   }
255
256   createFolderOrFile(name) {
257     let newFilesData: [any] = this.filesData;
258     let newFileNode = {
259       name: '',
260       data: ''
261     }
262     let newFileNode1 = {
263       name: '',
264       data: ''
265     }
266     for(let i=0; i< this.filesData.length; i++) {
267       if (this.filesData[i].name.includes(this.selectedFolder)) {
268         if(this.fileAction == 'createFolder') {          
269            newFileNode.name = this.filesData[i].name + name.srcElement.value + '/';
270            newFileNode.data = '';
271
272            newFileNode1.name = this.filesData[i].name + name.srcElement.value + '/README.md'
273            newFileNode1.data = name.srcElement.value + ' Folder';
274         } else {
275            newFileNode.name = this.filesData[i].name + name.srcElement.value;
276            newFileNode.data = '';
277         }
278         break;
279       }
280     }
281
282     this.filesData.splice(this.findIndexForNewNode()+1, 0, newFileNode);
283     this.filesData.splice(this.findIndexForNewNode()+1, 0, newFileNode1);
284     this.arrangeTreeData(this.filesData);
285   }
286
287   findIndexForNewNode() {
288     let indexForNewNode;
289     for(let i=0; i< this.filesData.length; i++) {
290       if (this.filesData[i].name.includes(this.selectedFolder)) {
291          indexForNewNode = i;
292       }
293     }
294     return indexForNewNode;
295   }
296
297   arrangeTreeData(paths) {
298     const tree = [];
299
300     paths.forEach((path) => {
301
302       const pathParts = path.name.split('/');
303       // pathParts.shift();
304       let currentLevel = tree;
305
306       pathParts.forEach((part) => {
307         const existingPath = currentLevel.filter(level => level.name === part);
308
309         if (existingPath.length > 0) {
310           currentLevel = existingPath[0].children;
311         } else {
312           const newPart = {
313             name: part,
314             children: [],
315             data: path.data,
316             path : path.name
317           };
318           if(part.trim() == this.blueprintName.trim()) { 
319             this.activationBlueprint = path.data; 
320             newPart.data = JSON.parse(this.activationBlueprint.toString());            
321             console.log('newpart', newPart);
322           }
323           if(newPart.name !== '') {           
324               currentLevel.push(newPart);
325               currentLevel = newPart.children;
326           }
327         }
328       });
329     });
330     this.dataSource.data = tree;
331     this.filesTree = tree;
332     this.isNameTextboxEnablled = false;
333     this.updateBlueprint();
334   }
335
336   enableNameInputEl(action) {    
337     this.fileAction = action;
338     if (action == 'createFolder' || action == 'createFile') {      
339       this.isNameTextboxEnablled = true;
340     }
341   }
342
343   deleteFolderOrFile(action) {
344     for(let i=0;i< this.filesData.length ; i++) {
345       if(this.filesData[i].name.includes(this.filetoDelete.trim())) {
346         this.filesData.splice(i, 1);
347         i = i-1;
348       }
349     }
350     this.arrangeTreeData(this.filesData);
351   }
352 }