@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.
- package/build/AssetCommand.d.ts +4 -0
- package/build/AssetCommand.js +293 -0
- package/build/Cmd.d.ts +2 -1
- package/build/Cmd.js +3 -3
- package/build/DBClient.d.ts +1 -0
- package/build/DBClient.js +51 -17
- package/build/ModuleCommand.js +28 -6
- package/build/TableCommand.js +235 -45
- package/build/index.js +3 -0
- package/package.json +6 -3
- package/pgdata/entry.csv +22 -0
- package/pgdata/setting.csv +4 -0
- package/src/AssetCommand.ts +307 -0
- package/src/Cmd.ts +4 -3
- package/src/DBClient.ts +70 -31
- package/src/ModuleCommand.ts +31 -5
- package/src/TableCommand.ts +269 -56
- package/src/index.ts +3 -0
- package/test/cyklang.xsd +439 -0
- package/test/t0.output +4 -0
- package/test/t0.xml +26 -0
- package/test/t01.output +32 -0
- package/test/t01.xml +185 -0
- package/test/t02.output +10 -0
- package/test/t02.xml +88 -0
- package/test/t03.output +9 -0
- package/test/t03.xml +74 -0
- package/test/t04.output +29 -0
- package/test/t04.xml +116 -0
- package/test/t05.output +18 -0
- package/test/t05.xml +41 -0
- package/test/t06.output +4 -0
- package/test/t06.xml +16 -0
- package/test/t07.output +5 -0
- package/test/t07.xml +82 -0
- package/test/t08.output +8 -0
- package/test/t08.xml +57 -0
- package/test/t09.output +10 -0
- package/test/t09.xml +29 -0
- package/test/t10.xml +125 -0
- package/test/t11.output +3 -0
- package/test/t11.xml +25 -0
- package/test/t12.output +7 -0
- package/test/t12.xml +42 -0
- package/test/t13.xml +23 -0
- package/test/t14.output +50 -0
- package/test/t14.xml +82 -0
- package/test/t14.xsd +102 -0
- package/test/t15.xml +47 -0
- package/test/t16.output +49 -0
- package/test/t16.xml +86 -0
- package/test/t17.output +5 -0
- package/test/t17.xml +21 -0
- package/test/t18.output +22 -0
- package/test/t18.xml +59 -0
- package/test/t19.xml +234 -0
- package/test/t20.xml +15 -0
- package/test/t21.output +21 -0
- package/test/t21.xml +71 -0
- package/test/t22.xml +23 -0
- package/test/t23.xml +44 -0
- package/test/t24.xml +10 -0
- 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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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.
|
|
119
|
-
column.width
|
|
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.
|
|
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.
|
|
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.
|
|
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 (
|
|
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.
|
|
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 &&
|
|
187
|
-
if (
|
|
188
|
-
result
|
|
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
|
-
|
|
195
|
-
|
|
196
|
-
|
|
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
|
}
|
package/src/ModuleCommand.ts
CHANGED
|
@@ -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
|
|
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)
|
|
@@ -156,7 +158,7 @@ class ModuleDownload extends Cmd {
|
|
|
156
158
|
}
|
|
157
159
|
const filePath = dbname + '.xml'
|
|
158
160
|
if (fs.existsSync(filePath) === true) {
|
|
159
|
-
logger.info('file ' + filePath + ' already exists
|
|
161
|
+
logger.info('file ' + filePath + ' already exists. Remove it before launching the command if you want to overwrite.')
|
|
160
162
|
}
|
|
161
163
|
else {
|
|
162
164
|
fs.writeFileSync(filePath, dbModule.source || '')
|
|
@@ -191,3 +193,27 @@ class ModuleList extends Cmd {
|
|
|
191
193
|
}
|
|
192
194
|
}
|
|
193
195
|
|
|
196
|
+
class ModuleDelete extends Cmd {
|
|
197
|
+
constructor(name: string, description: string) {
|
|
198
|
+
super(name)
|
|
199
|
+
this.description(description)
|
|
200
|
+
.argument('[files...]', 'module(s) to delete from the database')
|
|
201
|
+
.action(async(files: any, options: any) => {
|
|
202
|
+
await this.commandDelete(files, options)
|
|
203
|
+
})
|
|
204
|
+
}
|
|
205
|
+
async commandDelete(files: string[], options: any) {
|
|
206
|
+
try {
|
|
207
|
+
await this.prologue(options)
|
|
208
|
+
if (this.dbManager === undefined) throw 'dbManager undefined'
|
|
209
|
+
|
|
210
|
+
if (files.length === 0) throw 'no module to delete'
|
|
211
|
+
|
|
212
|
+
|
|
213
|
+
}
|
|
214
|
+
catch (err){
|
|
215
|
+
logger.error(err)
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
|