@cyklang/cli 0.1.2 → 0.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (63) hide show
  1. package/build/AssetCommand.d.ts +4 -0
  2. package/build/AssetCommand.js +293 -0
  3. package/build/Cmd.d.ts +2 -1
  4. package/build/Cmd.js +3 -3
  5. package/build/DBClient.d.ts +1 -0
  6. package/build/DBClient.js +51 -17
  7. package/build/ModuleCommand.js +28 -6
  8. package/build/TableCommand.js +235 -45
  9. package/build/index.js +3 -0
  10. package/package.json +6 -3
  11. package/pgdata/entry.csv +22 -0
  12. package/pgdata/setting.csv +4 -0
  13. package/src/AssetCommand.ts +307 -0
  14. package/src/Cmd.ts +4 -3
  15. package/src/DBClient.ts +70 -31
  16. package/src/ModuleCommand.ts +31 -5
  17. package/src/TableCommand.ts +269 -56
  18. package/src/index.ts +3 -0
  19. package/test/cyklang.xsd +439 -0
  20. package/test/t0.output +4 -0
  21. package/test/t0.xml +26 -0
  22. package/test/t01.output +32 -0
  23. package/test/t01.xml +185 -0
  24. package/test/t02.output +10 -0
  25. package/test/t02.xml +88 -0
  26. package/test/t03.output +9 -0
  27. package/test/t03.xml +74 -0
  28. package/test/t04.output +29 -0
  29. package/test/t04.xml +116 -0
  30. package/test/t05.output +18 -0
  31. package/test/t05.xml +41 -0
  32. package/test/t06.output +4 -0
  33. package/test/t06.xml +16 -0
  34. package/test/t07.output +5 -0
  35. package/test/t07.xml +82 -0
  36. package/test/t08.output +8 -0
  37. package/test/t08.xml +57 -0
  38. package/test/t09.output +10 -0
  39. package/test/t09.xml +29 -0
  40. package/test/t10.xml +125 -0
  41. package/test/t11.output +3 -0
  42. package/test/t11.xml +25 -0
  43. package/test/t12.output +7 -0
  44. package/test/t12.xml +42 -0
  45. package/test/t13.xml +23 -0
  46. package/test/t14.output +50 -0
  47. package/test/t14.xml +82 -0
  48. package/test/t14.xsd +102 -0
  49. package/test/t15.xml +47 -0
  50. package/test/t16.output +49 -0
  51. package/test/t16.xml +86 -0
  52. package/test/t17.output +5 -0
  53. package/test/t17.xml +21 -0
  54. package/test/t18.output +22 -0
  55. package/test/t18.xml +59 -0
  56. package/test/t19.xml +234 -0
  57. package/test/t20.xml +15 -0
  58. package/test/t21.output +21 -0
  59. package/test/t21.xml +71 -0
  60. package/test/t22.xml +23 -0
  61. package/test/t23.xml +44 -0
  62. package/test/t24.xml +10 -0
  63. package/test/test.json +113 -0
@@ -1,10 +1,14 @@
1
1
  #!/usr/bin/env ts-node
2
- import { DBManager } from "@cyklang/core"
2
+ import { DBManager, DBTable } from "@cyklang/core"
3
3
  import * as fs from "fs"
4
4
  import loglevel from 'loglevel'
5
5
  import { Command } from "commander"
6
6
  import { spawn } from "child_process"
7
7
  import { Cmd } from "./Cmd"
8
+ import { DBClient } from "./DBClient"
9
+ import { Stream } from "form-data"
10
+ import FormData from 'form-data'
11
+
8
12
  const logger = loglevel.getLogger("TableCommand.ts")
9
13
  logger.setLevel("debug")
10
14
 
@@ -13,40 +17,158 @@ export class TableCommand extends Command {
13
17
  super(name)
14
18
  this.description('import/export table rows').version('0.1')
15
19
  this.addCommand(new TableExportCmd())
20
+ this.addCommand(new TableImportCmd())
21
+ this.addCommand(new TableList('list', 'list database tables'))
22
+ this.addCommand(new TableList('l', '(l)ist database tables'))
23
+ this.addCommand(new TableQuery('query', 'query select table content'))
24
+ this.addCommand(new TableQuery('q', 'query select table content'))
16
25
  }
17
26
  }
18
27
 
19
28
  class TableExportCmd extends Cmd {
29
+
20
30
  constructor() {
21
31
  super('export')
22
32
  this.description('export to CSV file(s)')
23
- .argument('<tables...>', 'table(s) to export')
24
- .option('-f --file <file>', 'name of the file to create, defaults to <table_name>.csv')
25
- .option('-d --dir <directory>', 'name of the directory where to create exported files')
26
- .action(async (tables, options) => this.commandExport( tables, options))
33
+ .argument('<tables...>', 'table(s) to export')
34
+ .option('-f --file <file>', 'name of the file to create, defaults to <table_name>.csv')
35
+ .option('-d --dir <directory>', 'name of the directory where to create exported files')
36
+ .action(async (tables, options) => this.commandExport(tables, options))
27
37
  }
38
+
28
39
  async commandExport(tables: string[], options: Options) {
29
40
  await this.prologue(options)
30
41
  if (this.dbManager === undefined) throw 'dbManager undefined'
31
- await commandImportExport(this.dbManager, 'to', tables, options)
42
+ // await commandImportExport(this.dbManager, 'to', tables, options)
43
+
44
+ try {
45
+ if (options.file === undefined && options.dir === undefined) throw '--file or --dir mandatory'
46
+ if (options.file !== undefined && options.dir !== undefined) throw '--file and --dir are mutually exclusive'
47
+ if (tables.length === 0) throw 'at least one table name is mandatory'
48
+ if (tables.length > 1 && options.dir === undefined) throw 'for multiple tables, --dir is mandatory'
49
+
50
+ for (let indi = 0; indi < tables.length; indi++) {
51
+ const tableName = tables[indi]
52
+ const dbTable = await this.dbManager.dbTableExist(tableName)
53
+ if (dbTable === undefined) {
54
+ logger.info('table ' + tableName + ' not found')
55
+ continue
56
+ }
57
+ const filename = buildFilename(dbTable.name, options)
58
+ await this.copyTo(dbTable, filename)
59
+ }
60
+ }
61
+ catch (err) {
62
+ logger.error(err)
63
+ }
64
+ }
65
+
66
+ async copyTo(dbTable: DBTable, filename: string) {
67
+ if (this.dbRemote === undefined) throw 'dbRemote undefined'
68
+
69
+ try {
70
+ const writeStream = fs.createWriteStream(filename)
71
+
72
+ const response = await this.dbRemote.apiServer.get('/admin/export/' + dbTable.name, { responseType: 'stream' })
73
+ const stream = response as Stream
74
+ stream.on('data', (data: Uint8Array) => {
75
+ logger.debug('**** DATA ****', data.length)
76
+ const textDecoder = new TextDecoder()
77
+ const chunk = (textDecoder.decode(data))
78
+ writeStream.write(chunk)
79
+ })
80
+ stream.on('end', () => logger.debug('**** DONE ****'))
81
+ }
82
+ catch (err) {
83
+ logger.error(err)
84
+ }
32
85
  }
86
+
33
87
  }
34
88
 
35
89
  class TableImportCmd extends Cmd {
90
+
36
91
  constructor() {
37
92
  super('import')
38
93
  this.description('import from CSV file(s)')
39
- .option('-f --file <file>', 'name of the file to import, defaults to <table_name>.csv')
40
- .option('-d --dir <directory>', 'name of the directory where to find files to import')
41
- .action(async (tables, options) => this.commandImport(tables, options))
94
+ .argument('<tables...>', 'table(s) to import')
95
+ .option('-f --file <file>', 'name of the file to import, defaults to <table_name>.csv')
96
+ .option('-d --dir <directory>', 'name of the directory where to find files to import')
97
+ .action(async (tables, options) => this.commandImport(tables, options))
42
98
  }
99
+
43
100
  async commandImport(tables: string[], options: Options) {
44
101
  await this.prologue(options)
45
102
  if (this.dbManager === undefined) throw 'dbManager undefined'
46
- await commandImportExport(this.dbManager, 'from', tables, options)
103
+ // await commandImportExport(this.dbManager, 'to', tables, options)
104
+
105
+ try {
106
+ if (options.file === undefined && options.dir === undefined) throw '--file or --dir mandatory'
107
+ if (options.file !== undefined && options.dir !== undefined) throw '--file and --dir are mutually exclusive'
108
+ if (tables.length === 0) throw 'at least one table name is mandatory'
109
+ if (tables.length > 1 && options.dir === undefined) throw 'for multiple tables, --dir is mandatory'
110
+
111
+ for (let indi = 0; indi < tables.length; indi++) {
112
+ const tableName = tables[indi]
113
+ const dbTable = await this.dbManager.dbTableExist(tableName)
114
+ if (dbTable === undefined) {
115
+ logger.info('table ' + tableName + ' not found')
116
+ continue
117
+ }
118
+ const filename = buildFilename(dbTable.name, options)
119
+ this.copyFrom(dbTable, filename)
120
+ }
121
+ }
122
+ catch (err) {
123
+ logger.error(err)
124
+ }
125
+ }
126
+
127
+ async copyFrom(dbTable: DBTable, filename: string) {
128
+ if (this.dbRemote === undefined) throw 'dbRemote undefined'
129
+
130
+ try {
131
+ const file = fs.createReadStream(filename)
132
+ const form = new FormData()
133
+ form.append('uploadFile', file)
134
+
135
+ const route = '/admin/import/' + dbTable.name
136
+ const resp = await this.dbRemote.apiServer.post(route, form)
137
+ if (resp.status === 200) {
138
+ logger.debug(filename + ' uploaded')
139
+ }
140
+ }
141
+ catch (err) {
142
+ logger.error(err)
143
+ }
47
144
  }
48
145
  }
49
146
 
147
+
148
+ function buildFilename(tableName: string, options: Options): string {
149
+ let filename
150
+ if (options.file !== undefined) {
151
+ if (fs.existsSync(options.file) && fs.lstatSync(options.file).isDirectory())
152
+ throw '--file ' + options.file + ' is a directory'
153
+ filename = options.file
154
+ }
155
+ else if (options.dir === undefined)
156
+ throw '--file and --dir unspecified'
157
+ else if (fs.existsSync(options.dir) === false)
158
+ throw 'directory ' + options.dir + ' does not exist'
159
+ else if (fs.lstatSync(options.dir).isFile()) {
160
+ throw ("--dir " + options.dir + " is a file ")
161
+ }
162
+ else {
163
+ let directory = options.dir.trim()
164
+ if (directory[directory.length - 1] !== '/') {
165
+ directory += '/'
166
+ }
167
+ filename = directory + tableName + '.csv'
168
+ }
169
+ return filename
170
+ }
171
+
50
172
  interface Options {
51
173
  env: string | undefined
52
174
  file: string | undefined
@@ -69,59 +191,150 @@ const commandImportExport = async (dbManager: DBManager, direction: 'from' | 'to
69
191
  logger.info('table ' + tableName + ' not found')
70
192
  continue
71
193
  }
72
- let lcols: string | undefined
73
- for (let indj = 0; indj < dbTable.columns.length; indj++) {
74
- const dbColumn = dbTable.columns[indj]
75
- if (lcols === undefined) {
76
- lcols = "("
77
- }
78
- else {
79
- lcols += ","
80
- }
81
- lcols += dbColumn.name
82
- }
83
- lcols += ")"
194
+ psqlCmd(dbTable, direction, options)
195
+ }
196
+ }
197
+ catch (err) {
198
+ logger.error(err)
199
+ }
200
+ }
84
201
 
85
- let psql_cmd = "psql -c"
86
- if (process.env.HEROKU_CLI !== undefined) {
87
- psql_cmd = process.env.HEROKU_CLI + ' -c '
88
- }
89
202
 
90
- let filename
91
- if (options.file !== undefined) {
92
- if (fs.existsSync(options.file) && fs.lstatSync(options.file).isDirectory())
93
- throw '--file ' + options.file + ' is a directory'
94
- filename = options.file
95
- }
96
- else if (options.dir === undefined)
97
- throw '--file and --dir unspecified'
98
- else if (fs.existsSync(options.dir) === false)
99
- throw 'directory ' + options.dir + ' does not exist'
100
- else if (fs.lstatSync(options.dir).isFile()) {
101
- logger.debug("--dir " + options.dir + " is a file ")
102
- }
103
- else {
104
- let directory = options.dir.trim()
105
- if (directory[directory.length - 1] !== '/') {
106
- directory += '/'
107
- }
108
- filename = directory + tableName + '.csv'
109
- }
203
+ //--------------------------------------------------------------------------------------------------------------------
204
+ // function psqlCmd
205
+ // invoke local install of psql CLI
206
+ //--------------------------------------------------------------------------------------------------------------------
110
207
 
111
- let copy_cmd = "\\copy " + tableName + lcols + direction + " '" + filename + "' csv header"
112
- const shell_cmd = psql_cmd + '"' + copy_cmd + '"'
113
- logger.debug(shell_cmd)
114
- const child = spawn(shell_cmd.trim(), { stdio: 'inherit', shell: true })
115
- // exec(shell_cmd.trim(), (error, stdout, stderr) => {
116
- // if (error) throw error
117
- // logger.debug(stdout, stderr)
118
- // })
208
+ function psqlCmd(dbTable: DBTable, direction: 'from' | 'to', options: Options,) {
209
+
210
+ let lcols: string | undefined
211
+ for (let indj = 0; indj < dbTable.columns.length; indj++) {
212
+ const dbColumn = dbTable.columns[indj]
213
+ if (lcols === undefined) {
214
+ lcols = "("
215
+ }
216
+ else {
217
+ lcols += ","
119
218
  }
219
+ lcols += dbColumn.name
120
220
  }
121
- catch (err) {
122
- logger.error(err)
221
+ lcols += ")"
222
+
223
+ let psql_cmd = "psql -c"
224
+ if (process.env.HEROKU_CLI !== undefined) {
225
+ psql_cmd = process.env.HEROKU_CLI + ' -c '
226
+ }
227
+
228
+ let filename
229
+ if (options.file !== undefined) {
230
+ if (fs.existsSync(options.file) && fs.lstatSync(options.file).isDirectory())
231
+ throw '--file ' + options.file + ' is a directory'
232
+ filename = options.file
233
+ }
234
+ else if (options.dir === undefined)
235
+ throw '--file and --dir unspecified'
236
+ else if (fs.existsSync(options.dir) === false)
237
+ throw 'directory ' + options.dir + ' does not exist'
238
+ else if (fs.lstatSync(options.dir).isFile()) {
239
+ logger.debug("--dir " + options.dir + " is a file ")
240
+ }
241
+ else {
242
+ let directory = options.dir.trim()
243
+ if (directory[directory.length - 1] !== '/') {
244
+ directory += '/'
245
+ }
246
+ filename = directory + dbTable.name + '.csv'
247
+ }
248
+
249
+ let copy_cmd = "\\copy " + dbTable.name + lcols + direction + " '" + filename + "' csv header"
250
+ const shell_cmd = psql_cmd + '"' + copy_cmd + '"'
251
+ logger.debug(shell_cmd)
252
+ const child = spawn(shell_cmd.trim(), { stdio: 'inherit', shell: true })
253
+
254
+ }
255
+
256
+ //--------------------------------------------------------------------------------------------------------------------
257
+ // class TableList
258
+ //--------------------------------------------------------------------------------------------------------------------
259
+
260
+ class TableList extends Cmd {
261
+ constructor(name: string, description: string) {
262
+ super(name)
263
+ this.description(description)
264
+ .option('-s --sort <columns>', 'sort list by column numbers (begins with 0) separated by comma')
265
+ .action(async (options: any) => {
266
+ await this.commandList(options)
267
+ })
123
268
  }
124
269
 
270
+ async commandList(options: any) {
271
+ await this.prologue(options)
272
+ if (this.dbManager === undefined) throw 'dbManager undefined'
273
+
274
+ const dbClient = new DBClient(this.dbManager)
275
+ dbClient.selectFromTable('List of Tables', 'cyk_table',
276
+ { fields: 'table_id,table_name,table_description,table_access', sort: options.sort || '1' }
277
+ )
278
+ }
279
+ }
280
+
281
+ //--------------------------------------------------------------------------------------------------------------------
282
+ // class TableQuery
283
+ //--------------------------------------------------------------------------------------------------------------------
284
+
285
+ class TableQuery extends Cmd {
286
+
287
+ constructor(name: string, description: string) {
288
+ super(name)
289
+ this.description(description)
290
+ .argument('<table>', 'table to query')
291
+ .option('--where <clause_where_sql>', 'criteria in SQL syntax')
292
+ .option('-s --sort <columns>', 'sort by columns positions (begins by 0) separated by comma')
293
+ .option('-w --width <colwidth>', 'columns widths separated by comma')
294
+ .action(async (table, options) => {
295
+ this.commandQuery(table, options)
296
+ })
297
+ }
298
+
299
+ async commandQuery(table: string, options: any) {
300
+ try {
301
+ await this.prologue(options)
302
+ if (this.dbManager === undefined) throw 'dbManager undefined'
303
+
304
+ const dbTable = await this.dbManager.dbTableExist(table)
305
+ if (dbTable === undefined) throw 'table ' + table + ' not found'
125
306
 
307
+ let fields = ''
308
+
309
+ for (let ind = 0; ind < dbTable.columns.length; ind++) {
310
+ const dbColumn = dbTable.columns[ind]
311
+ let ok = false
312
+ let fieldName = dbColumn.name
313
+ if (dbColumn.dbType === 'text' || dbColumn.dbType === 'bytea') {
314
+ ok = false
315
+ fieldName = 'length(' + dbColumn.name + ')'
316
+ }
317
+ else {
318
+ ok = true
319
+ }
320
+ if (ok === true) {
321
+ if (fields !== '') fields += ','
322
+ fields += fieldName
323
+ }
324
+ }
325
+
326
+ logger.debug('commandQuery', table)
327
+ const dbClient = new DBClient(this.dbManager)
328
+ let title = 'Table ' + table
329
+ if (options.where !== undefined) {
330
+ title += ' where ' + options.where
331
+ }
332
+ dbClient.selectFromTable(title, table,
333
+ { fields: fields, width: options.width, sort: options.sort || '1', where: options.where })
334
+ }
335
+ catch (err) {
336
+ logger.error(err)
337
+ }
338
+ }
126
339
  }
127
340
 
package/src/index.ts CHANGED
@@ -8,6 +8,7 @@ import { TableCommand } from './TableCommand'
8
8
  import { TestCommand } from './TestCommand'
9
9
  import { RunCommand } from './RunCommand'
10
10
  import { InitCommand } from './InitCommand'
11
+ import { AssetCommand } from './AssetCommand'
11
12
  const logger = loglevel.getLogger("index.ts")
12
13
  logger.setLevel("debug")
13
14
 
@@ -16,6 +17,8 @@ program.name('cyk').description('cyklang CLI')
16
17
  .version('0.2')
17
18
 
18
19
  program.addCommand(new InitCommand('init'))
20
+ program.addCommand(new AssetCommand('asset', 'manage assets'))
21
+ program.addCommand(new AssetCommand('a', 'manage assets'))
19
22
  program.addCommand(new ModuleCommand('module'))
20
23
  program.addCommand(new ModuleCommand('m'))
21
24
  program.addCommand(new UserCommand('user'))