@cyklang/cli 0.1.1 → 0.2.0

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 (66) 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/InitCommand.js +1 -1
  8. package/build/ModuleCommand.js +7 -6
  9. package/build/TableCommand.js +235 -45
  10. package/build/index.js +3 -0
  11. package/package.json +5 -2
  12. package/pgdata/entry.csv +22 -0
  13. package/pgdata/setting.csv +4 -0
  14. package/src/AssetCommand.ts +307 -0
  15. package/src/Cmd.ts +4 -3
  16. package/src/DBClient.ts +70 -31
  17. package/src/InitCommand.ts +1 -1
  18. package/src/ModuleCommand.ts +8 -5
  19. package/src/TableCommand.ts +269 -56
  20. package/src/index.ts +3 -0
  21. package/src/index.xml +10 -0
  22. package/test/cyklang.xsd +439 -0
  23. package/test/t0.output +4 -0
  24. package/test/t0.xml +26 -0
  25. package/test/t01.output +32 -0
  26. package/test/t01.xml +185 -0
  27. package/test/t02.output +10 -0
  28. package/test/t02.xml +88 -0
  29. package/test/t03.output +9 -0
  30. package/test/t03.xml +74 -0
  31. package/test/t04.output +29 -0
  32. package/test/t04.xml +116 -0
  33. package/test/t05.output +18 -0
  34. package/test/t05.xml +41 -0
  35. package/test/t06.output +4 -0
  36. package/test/t06.xml +16 -0
  37. package/test/t07.output +5 -0
  38. package/test/t07.xml +82 -0
  39. package/test/t08.output +8 -0
  40. package/test/t08.xml +57 -0
  41. package/test/t09.output +10 -0
  42. package/test/t09.xml +29 -0
  43. package/test/t10.xml +125 -0
  44. package/test/t11.output +3 -0
  45. package/test/t11.xml +25 -0
  46. package/test/t12.output +7 -0
  47. package/test/t12.xml +42 -0
  48. package/test/t13.xml +23 -0
  49. package/test/t14.output +50 -0
  50. package/test/t14.xml +82 -0
  51. package/test/t14.xsd +102 -0
  52. package/test/t15.xml +47 -0
  53. package/test/t16.output +49 -0
  54. package/test/t16.xml +86 -0
  55. package/test/t17.output +5 -0
  56. package/test/t17.xml +21 -0
  57. package/test/t18.output +22 -0
  58. package/test/t18.xml +59 -0
  59. package/test/t19.xml +234 -0
  60. package/test/t20.xml +15 -0
  61. package/test/t21.output +21 -0
  62. package/test/t21.xml +71 -0
  63. package/test/t22.xml +23 -0
  64. package/test/t23.xml +44 -0
  65. package/test/t24.xml +10 -0
  66. package/test/test.json +113 -0
@@ -0,0 +1,307 @@
1
+ import { Command } from "commander"
2
+ import { getLogger, resetLevel } from "loglevel"
3
+ import { Cmd } from "./Cmd"
4
+ import { DBClient } from "./DBClient"
5
+ import * as fs from "fs"
6
+ import path from "path"
7
+ import mime from 'mime-types'
8
+ import readline from 'readline'
9
+ import { DBAsset, DBExecuteRequest, ObjectData, parseXML, PrimitiveData } from "@cyklang/core"
10
+ import FormData from 'form-data'
11
+
12
+ const logger = getLogger('AssetCommand.ts')
13
+ logger.setLevel('debug')
14
+
15
+ export class AssetCommand extends Command {
16
+ constructor(name: string, description: string) {
17
+ super(name)
18
+ this.description(description)
19
+ this.addCommand(new AssetList('list', 'list assets'))
20
+ this.addCommand(new AssetList('l', '(l)ist assets'))
21
+ this.addCommand(new AssetUpload('upload', 'upload assets from current directory'))
22
+ this.addCommand(new AssetUpload('u', '(u)pload assets'))
23
+ }
24
+ }
25
+
26
+ class AssetList extends Cmd {
27
+ constructor(name: string, description: string) {
28
+ super(name)
29
+ this.description(description)
30
+ .option('-s --sort <columns>', 'sort list by column numbers (begins by 0) separated by comma')
31
+ .action(async (options: any) => { await this.commandList(options) })
32
+ }
33
+ async commandList(options: any) {
34
+ try {
35
+ await this.prologue(options)
36
+ if (this.dbManager === undefined) throw 'dbManager undefined'
37
+
38
+ const dbClient = new DBClient(this.dbManager)
39
+ dbClient.selectFromTable('List of Assets', 'cyk_asset',
40
+ { fields: 'asset_id,asset_route,asset_last_update,asset_mimetype', sort: options.sort || '1' }
41
+ )
42
+ }
43
+ catch (err) {
44
+ logger.error(err)
45
+ }
46
+ }
47
+ }
48
+
49
+ //----------------------------------------------------------------------------------------------
50
+ // class AssetUpload
51
+ //----------------------------------------------------------------------------------------------
52
+
53
+ interface FileDescriptor {
54
+ path: string
55
+ mtime: Date
56
+ }
57
+ class AssetUpload extends Cmd {
58
+
59
+ constructor(name: string, description: string) {
60
+ super(name)
61
+ this.description(description)
62
+ .option('-d --dest <destination>', 'route of destination')
63
+ .argument('[sources...]', 'local files or directories to upload as assets to the server')
64
+ .action(async (sources: any, options: any) => {
65
+ await this.commandUpload(sources, options)
66
+ })
67
+ }
68
+
69
+ async commandUpload(sources: string[], options: any) {
70
+
71
+ try {
72
+ await this.prologue(options)
73
+ if (this.dbManager === undefined) throw 'dbManager undefined'
74
+
75
+ if (sources.length === 0) throw 'no source to upload'
76
+
77
+ let dest = "/"
78
+ if (options.dest !== undefined) dest = options.dest
79
+ if (dest.substring(0, 1) !== '/') dest = '/' + dest
80
+ if (dest.substring(dest.length - 1) !== '/') dest = dest + '/'
81
+
82
+ for (let ind = 0; ind < sources.length; ind++) {
83
+ const source = sources[ind]
84
+ if (fs.existsSync(source) === false) throw 'source ' + source + ' does not exist'
85
+ const filestat = fs.lstatSync(source)
86
+ if (filestat.isDirectory() === true) {
87
+ await this.uploadDirectory(source, dest)
88
+ }
89
+ else if (filestat.isFile() === true) {
90
+ const base = path.parse(source).base
91
+ logger.debug('upload file ' + source + ' to ' + dest + base)
92
+ await this.uploadAsset({path:source, mtime: filestat.mtime}, dest + base)
93
+ }
94
+ }
95
+ }
96
+ catch (err) {
97
+ logger.error(err)
98
+ }
99
+ }
100
+
101
+ //----------------------------------------------------------------------------------------------
102
+ // uploadDirectory
103
+ //----------------------------------------------------------------------------------------------
104
+
105
+ async uploadDirectory(source: string, dest: string) {
106
+
107
+ const localList: FileDescriptor[] = []
108
+ let dirName: string
109
+ if (source.substring(source.length - 1) === '/') {
110
+ // trailing directory separator
111
+ dirName = source
112
+ }
113
+ else {
114
+ dirName = source + '/'
115
+ }
116
+
117
+ logger.debug('dirName', dirName)
118
+
119
+ this.scanDir(dirName, localList)
120
+
121
+ const remoteList: FileDescriptor[] = []
122
+ await this.scanAssets(remoteList)
123
+
124
+ const uploadList = this.buildUploadList(dirName, dest, localList, remoteList)
125
+
126
+ // logger.info(fileNames.join('\n'))
127
+ if (uploadList.length === 0) throw 'None file to upload'
128
+
129
+ const rl = readline.createInterface(process.stdin, process.stdout)
130
+ rl.question('Do you want to upload this(these) ' + uploadList.length + ' file(s) (Y/N/y/n) ?', async (reply) => {
131
+ rl.close()
132
+ if (reply !== 'Y' && reply !== 'y') {
133
+ logger.info('upload cancelled')
134
+ return
135
+ }
136
+
137
+ await this.uploadFiles(dirName, uploadList, dest)
138
+ })
139
+ }
140
+
141
+ //----------------------------------------------------------------------------------------------
142
+ // buildUploadList
143
+ //----------------------------------------------------------------------------------------------
144
+
145
+ buildUploadList(dirName: string, dest: string, localList: FileDescriptor[], remoteList: FileDescriptor[]): FileDescriptor[] {
146
+ const result: FileDescriptor[] = []
147
+ const remoteMap = new Map<string, FileDescriptor>()
148
+ for(let ind = 0; ind < remoteList.length; ind++) {
149
+ const remote = remoteList[ind]
150
+ remoteMap.set(remote.path, remote)
151
+ }
152
+ for(let ind = 0; ind < localList.length; ind++ ) {
153
+ const localDesc = localList[ind]
154
+ const route = dest + localDesc.path.substring(dirName.length)
155
+ const remoteDesc = remoteMap.get(route)
156
+
157
+ if (remoteDesc === undefined || remoteDesc.mtime.getTime() + 1000 < localDesc.mtime.getTime()) {
158
+ if (remoteDesc === undefined) logger.debug('remoteDesc undefined')
159
+ else logger.debug('remoteDesc.mtime '+ remoteDesc.mtime.getTime(), 'localDesc.mtime ' + localDesc.mtime.getTime())
160
+ logger.info(localDesc.path)
161
+ result.push(localDesc)
162
+ }
163
+ }
164
+ return result
165
+ }
166
+
167
+
168
+ //----------------------------------------------------------------------------------------------
169
+ // scanAssets
170
+ //----------------------------------------------------------------------------------------------
171
+
172
+ async scanAssets(result: FileDescriptor[]) {
173
+
174
+ if (this.dbManager === undefined) throw 'dbManager undefined'
175
+ const dbReq = new DBExecuteRequest()
176
+ dbReq.selectFromTable = 'cyk_asset'
177
+ const pFields = dbReq.parameters.addVariable('fields', this.dbManager.scope.structure.stringDataType)
178
+ pFields.data = new PrimitiveData(this.dbManager.scope.structure.stringDataType, "asset_id,asset_route,asset_last_update,asset_mimetype")
179
+ const xmlResult = await this.dbManager.dbExecute(dbReq)
180
+ if (xmlResult === undefined || xmlResult === null) throw 'xmlResult null or undefined'
181
+ const tag = parseXML('scanAssets', xmlResult)
182
+ const objResult = await this.dbManager.scope.structure.objectDataType.parseData(tag, this.dbManager.scope) as ObjectData
183
+ const rows = (objResult.variables.getData('cyk_asset') as ObjectData).variables
184
+ for(let ind = 0; ind < rows.list.length; ind++) {
185
+ const {variable: row} = rows.list[ind]
186
+ const record = row.data as ObjectData
187
+ const asset_route = (record.variables.getData('asset_route') as PrimitiveData).value as string
188
+ const asset_last_update = (record.variables.getData('asset_last_update') as PrimitiveData).value as Date
189
+ // logger.debug('asset_route ' + asset_route,'asset_last_update ' + asset_last_update)
190
+ result.push({path: asset_route, mtime: asset_last_update})
191
+ }
192
+ }
193
+
194
+ //----------------------------------------------------------------------------------------------
195
+ // scanDir
196
+ //----------------------------------------------------------------------------------------------
197
+
198
+ scanDir(dirName: string, result: FileDescriptor[]) {
199
+ const files = fs.readdirSync(dirName)
200
+ for (let ind = 0; ind < files.length; ind++) {
201
+ const fileName = files[ind]
202
+ if (this.file2exclude(fileName) === true) continue
203
+ const fstat = fs.statSync(dirName + fileName)
204
+ if (fstat.isDirectory() === true) {
205
+ this.scanDir(dirName + fileName + '/', result)
206
+ }
207
+ else {
208
+ if (mime.lookup(fileName) === false) {
209
+ logger.info(fileName + ' file extension is unknown and will not be uploaded')
210
+ continue
211
+ }
212
+ //const dbAssetExist = this.dbManager?.dbAssetExist()
213
+ result.push({path: dirName + fileName, mtime: fstat.mtime})
214
+ // logger.debug(dirName + '/' + fileName)
215
+ }
216
+ }
217
+ }
218
+
219
+ //----------------------------------------------------------------------------------------------
220
+ // file2exclude
221
+ //----------------------------------------------------------------------------------------------
222
+
223
+ ignorePatterns = ['.git', 'node_modules']
224
+
225
+ file2exclude(fileName: string): boolean {
226
+ let result = false
227
+ this.ignorePatterns.forEach((pattern) => {
228
+ if (fileName === pattern) result = true
229
+ })
230
+ return result
231
+ }
232
+
233
+ //----------------------------------------------------------------------------------------------
234
+ // uploadFiles
235
+ //----------------------------------------------------------------------------------------------
236
+
237
+ async uploadFiles(dirName: string, uploadList: FileDescriptor[], dest: string) {
238
+ for (let ind = 0; ind < uploadList.length; ind++) {
239
+ const upload = uploadList[ind]
240
+ const route = dest + upload.path.substring(dirName.length)
241
+ logger.debug('upload ' + upload.path + ' to ' + route)
242
+ await this.uploadAsset(upload, route)
243
+ }
244
+ }
245
+
246
+ //----------------------------------------------------------------------------------------------
247
+ // uploadAsset
248
+ //----------------------------------------------------------------------------------------------
249
+
250
+ async uploadAsset(upload: FileDescriptor, route: string) {
251
+ logger.debug('uploadAsset ' + upload.path + ' to route ' + route + ' with mimetype ' + mime.lookup(upload.path))
252
+
253
+ try {
254
+ let dbAsset: DBAsset | undefined = await this.dbManager?.dbAssetExist(route)
255
+ if (dbAsset === undefined) {
256
+ dbAsset = new DBAsset()
257
+ }
258
+ const mimetype = mime.lookup(upload.path)
259
+ if (mimetype === false) throw 'mimetype false for ' + upload
260
+ dbAsset.mimetype = mimetype
261
+ dbAsset.route = route
262
+
263
+ // const fstat = fs.statSync(fileName)
264
+ dbAsset.last_update = upload.mtime
265
+
266
+ // const contentBuffer = fs.readFileSync(fileName)
267
+ // dbAsset.content = contentBuffer
268
+ // logger.debug('dbAsset.content length ' + dbAsset.content.length)
269
+
270
+ if (dbAsset.id === undefined) {
271
+ dbAsset.id = await this.dbManager?.dbAssetInsert(dbAsset)
272
+ logger.info('inserted asset ' + dbAsset.id + ' route ' + dbAsset.route)
273
+ }
274
+ else {
275
+ await this.dbManager?.dbAssetUpdate(dbAsset)
276
+ logger.info('updated asset ' + dbAsset.route)
277
+ }
278
+
279
+ await this.uploadAssetContent(dbAsset, upload.path)
280
+ }
281
+ catch (err) {
282
+ logger.error(err)
283
+ }
284
+ }
285
+
286
+ //----------------------------------------------------------------------------------------------
287
+ // uploadAssetContent
288
+ //----------------------------------------------------------------------------------------------
289
+
290
+ async uploadAssetContent(dbAsset: DBAsset, fileName: string) {
291
+ logger.debug('uploadAssetContent file ' + fileName + ' to path ' + path)
292
+ try {
293
+ const file = fs.createReadStream(fileName)
294
+ const form = new FormData()
295
+ form.append('uploadFile', file)
296
+
297
+ const route = '/upload/cyk_asset/asset_content?asset_id=' + dbAsset.id
298
+ const resp = await this.dbRemote?.apiServer.post(route, form)
299
+ if ( resp.status === 200 ) {
300
+ logger.info(fileName + ' uploaded')
301
+ }
302
+ }
303
+ catch (err) {
304
+ logger.error(err)
305
+ }
306
+ }
307
+ }
package/src/Cmd.ts CHANGED
@@ -8,6 +8,7 @@ logger.setLevel('debug')
8
8
 
9
9
  export class Cmd extends Command {
10
10
  dbManager: DBManager | undefined
11
+ dbRemote: DBRemote | undefined
11
12
 
12
13
  constructor(name: string) {
13
14
  super(name);
@@ -23,11 +24,11 @@ export class Cmd extends Command {
23
24
  if (process.env.DBREMOTE_URL === undefined) throw 'DBREMOTE_URL undefined'
24
25
  logger.debug('DBREMOTE_URL ' + process.env.DBREMOTE_URL)
25
26
  const nodeCrypto = new NodeCrypto()
26
- const dbRemote = new DBRemote(structure.scope, process.env.DBREMOTE_URL, nodeCrypto)
27
- const login = await dbRemote.signin(process.env.USER_NAME, undefined, process.env.USER_PASSWORD)
27
+ this.dbRemote = new DBRemote(structure.scope, process.env.DBREMOTE_URL, nodeCrypto)
28
+ const login = await this.dbRemote.signin(process.env.USER_NAME, undefined, process.env.USER_PASSWORD)
28
29
  // logger.debug(login)
29
30
 
30
- this.dbManager = new DBManager(structure.scope, dbRemote)
31
+ this.dbManager = new DBManager(structure.scope, this.dbRemote)
31
32
  await this.dbManager.initialize()
32
33
  }
33
34
  }
package/src/DBClient.ts CHANGED
@@ -11,7 +11,7 @@ export class DBClient {
11
11
  }
12
12
 
13
13
  async selectFromTable(title: string, tableName: string, options:
14
- { fields: string, sort?: string | undefined, where?: string | undefined }) {
14
+ { fields: string, sort?: string | undefined, where?: string | undefined, width?: string | undefined }) {
15
15
 
16
16
  try {
17
17
 
@@ -26,13 +26,13 @@ export class DBClient {
26
26
  const colnames = options.fields.split(',')
27
27
  const sortcols = options.sort.split(',')
28
28
  let order_by = ''
29
- for (let ind = 0; ind < sortcols.length; ind++ ) {
29
+ for (let ind = 0; ind < sortcols.length; ind++) {
30
30
  const coli = sortcols[ind]
31
31
  const colname = colnames[Number(coli)]
32
32
  if (order_by !== '') order_by += ','
33
33
  order_by += colname
34
34
  }
35
- logger.debug('order_by '+order_by)
35
+ logger.debug('order_by ' + order_by)
36
36
  request.parameters.addVariable('order_by', stringDataType).data = new PrimitiveData(stringDataType, order_by)
37
37
  }
38
38
 
@@ -42,37 +42,38 @@ export class DBClient {
42
42
  // request.parameters.addVariable(key, stringDataType).data = data
43
43
  // })
44
44
  // }
45
-
45
+
46
46
  const xmlResult = await this.dbManager.dbExecute(request)
47
47
  if (xmlResult === undefined) throw 'xmlResult undefined'
48
48
  const tagXmlResult = parseXML('xmlResult', xmlResult)
49
-
49
+
50
50
  const objXmlResult = (await objectDataType.parseData(tagXmlResult, this.dbManager.scope)) as ObjectData
51
51
  const objectMeta = (objXmlResult.variables.getData('meta')) as ObjectData
52
52
  if (objectMeta === undefined) throw 'objectMeta not found'
53
53
  const xmlMeta = objectMeta.variables.getString(tableName)
54
54
  if (xmlMeta === undefined) throw 'meta does not have ' + tableName + ' description'
55
-
55
+
56
56
  const tagDBTable = parseXML('dbTable', xmlMeta)
57
57
  const dbTable = new DBTable(tagDBTable)
58
-
58
+
59
59
  const fields = options.fields.split(',')
60
60
  const list = new List()
61
-
61
+ if (options.width !== undefined) list.width = options.width
62
+
62
63
  for (let ind = 0; ind < fields.length; ind++) {
63
64
  const field = fields[ind]
64
65
  const dbColumn = dbTable.columns.filter((dbColumn) => dbColumn.name === field)[0]
65
66
  if (dbColumn === undefined) throw 'field ' + field + ' not found'
66
67
  list.addDBColumn(dbColumn)
67
68
  }
68
-
69
+
69
70
  const objDataset = objXmlResult.variables.getData(tableName) as ObjectData
70
71
  for (let ind = 0; ind < objDataset.variables.length(); ind++) {
71
72
  const record = (objDataset.variables.at(ind)?.data as ObjectData)
72
73
  list.addObjectData(record)
73
74
  }
74
75
  logger.info(list.renderList(title))
75
-
76
+
76
77
  }
77
78
  catch (err) {
78
79
  logger.error(err)
@@ -81,6 +82,7 @@ export class DBClient {
81
82
  }
82
83
 
83
84
  class List {
85
+ width = "0"
84
86
  columns: Column[] = []
85
87
  rows: Row[] = []
86
88
  lines: string[] = []
@@ -107,20 +109,44 @@ class List {
107
109
  }
108
110
 
109
111
  renderList(title: string): string {
110
- // calculate column.width
112
+
113
+ // parse width option
114
+
115
+ const colws: number[] = []
116
+ const colws_ = this.width.split(',')
117
+
118
+ for (let ind = 0; ind < colws_.length; ind++) {
119
+ const colw = Number.parseInt(colws_[ind])
120
+ if (isNaN(colw)) {
121
+ const msg = '--width incorrect format : ' + this.width
122
+ throw msg
123
+ }
124
+ colws.push(colw)
125
+ }
126
+
127
+ logger.debug('colws', colws.join(','))
128
+
129
+ // calculate column.actualWidth
111
130
  let total_width = 0
112
131
  for (let indi = 0; indi < this.columns.length; indi++) {
113
132
  const column = this.columns[indi]
114
- column.width = column.label.length
133
+
134
+ if (indi < colws.length) column.width = colws[indi]
135
+ else column.width = colws[colws.length - 1]
136
+
137
+ column.actualWidth = column.label.length
115
138
  this.rows.forEach((row) => {
116
139
  const cell = row.cells[indi]
117
140
  const renderedCell = this.renderCell(cell)
118
- if (renderedCell.length > column.width) {
119
- column.width = renderedCell.length
141
+ if (renderedCell.length > column.actualWidth) {
142
+ if (column.width === 0 || renderedCell.length < column.width)
143
+ column.actualWidth = renderedCell.length
144
+ else
145
+ column.actualWidth = column.width
120
146
  }
121
147
  })
122
148
  if (total_width > 0) total_width += 3
123
- total_width += column.width
149
+ total_width += column.actualWidth
124
150
  }
125
151
 
126
152
  // title
@@ -131,43 +157,46 @@ class List {
131
157
  this.lines.push(this.renderSeparator())
132
158
 
133
159
  // rows
134
- for (let indj = 0; indj < this.rows.length; indj++ ) {
160
+ for (let indj = 0; indj < this.rows.length; indj++) {
135
161
  const row = this.rows[indj]
136
162
  this.lines.push(this.renderRow(row))
137
163
  }
164
+ this.lines.push(this.renderSeparator())
165
+ this.lines.push('Number of lines : ' + this.rows.length)
166
+
138
167
  return this.lines.join('\n')
139
168
  }
140
169
 
141
170
  renderHeaderRow(): string {
142
171
  let result = ''
143
- for (let indi = 0; indi < this.columns.length; indi++ ) {
172
+ for (let indi = 0; indi < this.columns.length; indi++) {
144
173
  const column = this.columns[indi]
145
174
  if (result !== '') result += ' | '
146
- result += this.renderCell(column.label, column.width, 'center')
175
+ result += this.renderCell(column.label, column.actualWidth, 'center')
147
176
  }
148
177
  return result
149
178
  }
150
179
 
151
180
  renderRow(row: Row): string {
152
181
  let result = ''
153
- for (let indi = 0; indi < this.columns.length; indi++ ) {
182
+ for (let indi = 0; indi < this.columns.length; indi++) {
154
183
  const column = this.columns[indi]
155
184
  let justify: 'left' | 'right' | 'center' | undefined = 'left'
156
185
  if (column.dbColumn?.dataTypeName === 'number' || column?.type === 'number') {
157
186
  justify = 'right'
158
187
  }
159
188
  if (result !== '') result += ' | '
160
- result += this.renderCell(row.cells[indi], column.width, justify)
189
+ result += this.renderCell(row.cells[indi], column.actualWidth, justify)
161
190
  }
162
191
  return result
163
192
  }
164
193
 
165
194
  renderSeparator(): string {
166
195
  let result = ''
167
- for ( let indi = 0; indi < this.columns.length; indi++ ) {
196
+ for (let indi = 0; indi < this.columns.length; indi++) {
168
197
  const column = this.columns[indi]
169
198
  if (result !== '') result += '-+-'
170
- result += '-'.repeat(column.width)
199
+ result += '-'.repeat(column.actualWidth)
171
200
  }
172
201
  return result
173
202
  }
@@ -180,20 +209,29 @@ class List {
180
209
  else if (value === null) {
181
210
  result = 'null'
182
211
  }
212
+ else if (value instanceof Date) {
213
+ const d = value as Date
214
+ result = d.toISOString()
215
+ }
183
216
  else {
184
217
  result = value.toString()
185
218
  }
186
- if (width !== undefined && result.length < width) {
187
- if (justify === undefined || justify === 'left') {
188
- result += ' '.repeat(width - result.length)
189
- }
190
- else if (justify === 'right') {
191
- result = ' '.repeat(width - result.length) + result
219
+ if (width !== undefined && width !== 0) {
220
+ if (result.length >= width) {
221
+ result = result.substring(0, width)
192
222
  }
193
223
  else {
194
- // justify === center
195
- const before = Math.floor((width - result.length) / 2)
196
- result = ' '.repeat(before) + result + ' '.repeat(width - result.length - before)
224
+ if (justify === undefined || justify === 'left') {
225
+ result += ' '.repeat(width - result.length)
226
+ }
227
+ else if (justify === 'right') {
228
+ result = ' '.repeat(width - result.length) + result
229
+ }
230
+ else {
231
+ // justify === center
232
+ const before = Math.floor((width - result.length) / 2)
233
+ result = ' '.repeat(before) + result + ' '.repeat(width - result.length - before)
234
+ }
197
235
  }
198
236
  }
199
237
  return result
@@ -207,6 +245,7 @@ class Column {
207
245
  type?: string | undefined;
208
246
  computed?: string | undefined;
209
247
  width: number = 0
248
+ actualWidth: number = 0
210
249
  constructor(name: string) {
211
250
  this.name = name
212
251
  }
@@ -79,7 +79,7 @@ SECRET_RTOKEN="${this.generateToken()}"
79
79
  LOGIN_IMAGE="https://media.smartbox.com/pim/1000001967971201770883.jpg?thumbor=800x0/filters:quality(90)"
80
80
  LOGIN_TITLE="CYK Framework"
81
81
  LOGIN_SUBTITLE="Une application pilote du framework CYK"
82
- LOGIN_APPLI="accounting"
82
+ LOGIN_APPLI="index"
83
83
 
84
84
  # credential used by cyk CLI
85
85
 
@@ -31,9 +31,11 @@ Otherwise, a new module is inserted in the database. To rename a module, use opt
31
31
  }
32
32
 
33
33
  function getModuleDBName(filePath: string): string {
34
- let result
35
- if (filePath.endsWith('.xml') === false) throw filePath + ' does not have .xml extension'
36
- result = path.basename(filePath, '.xml')
34
+ // let result
35
+ // if (filePath.endsWith('.xml') === false) throw filePath + ' does not have .xml extension'
36
+ // result = path.basename(filePath, '.xml')
37
+
38
+ const result = path.parse(filePath).name
37
39
  const identifierRegex = /^[_a-zA-Z][_a-zA-Z0-9]*$/
38
40
  if (result.match(identifierRegex) === null) throw 'filename ' + result + ' does not match identifier syntax'
39
41
  return result
@@ -46,7 +48,7 @@ class ModuleUpload extends Cmd {
46
48
  constructor(name: string, description: string) {
47
49
  super(name)
48
50
  this.description(description)
49
- .option('-i --id <id>', 'module ID to download')
51
+ .option('-i --id <id>', 'module ID to upload')
50
52
  .argument('[files...]', 'local module file(s) to upload to the server')
51
53
  .action(async (files: any, options: any) => {
52
54
  await this.commandUpload(files, options)
@@ -88,6 +90,7 @@ class ModuleUpload extends Cmd {
88
90
  }
89
91
  else {
90
92
  // update
93
+ dbModuleExist.source = source
91
94
  dbModuleExist.access = tag.attributes.ACCESS
92
95
  dbModuleExist.description = tag.attributes.DESCRIPTION
93
96
  logger.debug('module update ' + dbname + ' access ' + dbModuleExist.access + ' description ' + dbModuleExist.description)
@@ -155,7 +158,7 @@ class ModuleDownload extends Cmd {
155
158
  }
156
159
  const filePath = dbname + '.xml'
157
160
  if (fs.existsSync(filePath) === true) {
158
- logger.info('file ' + filePath + ' already exists and will not be overwritten ')
161
+ logger.info('file ' + filePath + ' already exists. Remove it before launching the command if you want to overwrite.')
159
162
  }
160
163
  else {
161
164
  fs.writeFileSync(filePath, dbModule.source || '')