w-serv-orm 1.1.6 → 1.1.8
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/README.md +1 -0
- package/dist/w-serv-orm.umd.js +2 -2
- package/dist/w-serv-orm.umd.js.map +1 -1
- package/docs/WServOrm.html +20 -6
- package/docs/WServOrm.mjs.html +22 -4
- package/docs/index.html +1 -1
- package/g-mOrm.mjs +1 -0
- package/package.json +3 -3
- package/schema/tables/tests.mjs +1 -1
- package/src/WServOrm.mjs +21 -3
- package/src/genTestData.mjs +11 -3
- package/src/getDb.mjs +27 -6
- package/src/getDbBackupAndRecover.mjs +27 -2
- package/src/getItemsFail.mjs +5 -0
- package/src/getMapOrm.mjs +189 -37
- package/src/getProc.mjs +79 -70
- package/src/getSettings.mjs +1 -1
- package/test/api-procOrm.test.mjs +93 -0
- package/test/unit-WServOrm.test.mjs +554 -0
- package/test/unit-getDb.test.mjs +188 -0
- package/test/unit-mapOrm.test.mjs +373 -0
package/src/getMapOrm.mjs
CHANGED
|
@@ -2,27 +2,100 @@ import get from 'lodash-es/get.js'
|
|
|
2
2
|
import each from 'lodash-es/each.js'
|
|
3
3
|
import map from 'lodash-es/map.js'
|
|
4
4
|
import size from 'lodash-es/size.js'
|
|
5
|
+
import take from 'lodash-es/take.js'
|
|
6
|
+
import join from 'lodash-es/join.js'
|
|
7
|
+
import truncate from 'lodash-es/truncate.js'
|
|
5
8
|
import nowms2str from 'wsemi/src/nowms2str.mjs'
|
|
9
|
+
import genIDSeq from 'wsemi/src/genIDSeq.mjs'
|
|
6
10
|
import isarr from 'wsemi/src/isarr.mjs'
|
|
7
11
|
import iseobj from 'wsemi/src/iseobj.mjs'
|
|
8
12
|
import isestr from 'wsemi/src/isestr.mjs'
|
|
13
|
+
import isnum from 'wsemi/src/isnum.mjs'
|
|
9
14
|
import clearXSS from 'wsemi/src/clearXSS.mjs'
|
|
15
|
+
import getItemsFail from 'w-data-collector/src/getItemsFail.mjs'
|
|
10
16
|
|
|
11
17
|
|
|
12
|
-
|
|
13
|
-
|
|
18
|
+
function getMsgFail(msg, fails) {
|
|
19
|
+
//前端訊息僅列出失敗筆, 錯誤原文另由console.log輸出, 與既有整批錯誤之處理一致
|
|
20
|
+
//有效主鍵(非空字串或數字)列為id[...], 否則列其於輸入內之位置, 失敗筆數過多時僅列前nShow筆並附總筆數
|
|
21
|
+
//主鍵超過nIdMax字元者截斷顯示(LMDB逐筆失敗常因主鍵超長, 故兩者常同時出現), log仍保留全文
|
|
14
22
|
|
|
23
|
+
//nShow, nIdMax
|
|
24
|
+
let nShow = 10
|
|
25
|
+
let nIdMax = 100
|
|
26
|
+
|
|
27
|
+
//ss
|
|
28
|
+
let ss = map(take(fails, nShow), (v) => {
|
|
29
|
+
if (isestr(v.id) || isnum(v.id)) {
|
|
30
|
+
return `id[${truncate(String(v.id), { length: nIdMax, omission: '…' })}]`
|
|
31
|
+
}
|
|
32
|
+
return `data[${v.k}]`
|
|
33
|
+
})
|
|
34
|
+
|
|
35
|
+
//c
|
|
36
|
+
let c = `${msg}: ${join(ss, ', ')}`
|
|
37
|
+
if (size(fails) > nShow) {
|
|
38
|
+
c += `, ... (${size(fails)} in total)`
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
return c
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
function isPkIdString(dsTab, keys) {
|
|
46
|
+
//資料表之主鍵欄是否為id且可容納字串型之補值, 供adjustData判斷是否補主鍵
|
|
47
|
+
|
|
48
|
+
//check, 須有id欄; 有settings者另須標為pk
|
|
49
|
+
if (keys.indexOf('id') < 0) {
|
|
50
|
+
return false
|
|
51
|
+
}
|
|
52
|
+
let settings = get(dsTab, 'settings', null)
|
|
53
|
+
if (iseobj(settings) && get(settings, 'id.pk') !== true) {
|
|
54
|
+
return false
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
//check, 型別已知者須為字串型, 與w-orm-reladb可自動補值之主鍵欄型別一致
|
|
58
|
+
let type = get(settings, 'id.type', '') || get(dsTab, 'kpType.id', '')
|
|
59
|
+
if (isestr(type) && ['STRING', 'TEXT', 'CHAR', 'UUID', 'CITEXT'].indexOf(type.toUpperCase()) < 0) {
|
|
60
|
+
return false
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
return true
|
|
64
|
+
}
|
|
15
65
|
|
|
16
|
-
|
|
66
|
+
|
|
67
|
+
function getItemsIdXSS(data) {
|
|
68
|
+
//輸入數據之主鍵(非空字串)會被clearXSS改寫者, 回傳[{k,id}], k為其於輸入內之位置
|
|
69
|
+
//寫入路徑之欄位皆經clearXSS而select與del不經過, 主鍵被改寫後以原主鍵查詢與刪除皆找不到, 故呼叫端須於寫入前整批拒絕
|
|
70
|
+
let rs = []
|
|
71
|
+
each(isarr(data) ? data : [data], (v, k) => {
|
|
72
|
+
let id = get(v, 'id')
|
|
73
|
+
if (isestr(id) && clearXSS(id) !== id) {
|
|
74
|
+
rs.push({ k, id })
|
|
75
|
+
}
|
|
76
|
+
})
|
|
77
|
+
return rs
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
function getMsgIdXSS(msg, fails) {
|
|
82
|
+
//主鍵含HTML標記, 訊息只列位置不回顯主鍵, 主鍵原文另由console.log輸出
|
|
83
|
+
return getMsgFail(`${msg}, id contains HTML markup`, map(fails, (v) => {
|
|
84
|
+
return { k: v.k }
|
|
85
|
+
}))
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
function adjustData(ds, woName, userId, data, mode = 'save') { //mode: 'insert', 'save', 'insert+save'
|
|
17
90
|
|
|
18
91
|
//check
|
|
19
|
-
if (!iseobj(
|
|
20
|
-
console.log('ds',
|
|
92
|
+
if (!iseobj(ds)) {
|
|
93
|
+
console.log('ds', ds)
|
|
21
94
|
throw new Error('invalid ds')
|
|
22
95
|
}
|
|
23
96
|
|
|
24
97
|
//keys
|
|
25
|
-
let keys =
|
|
98
|
+
let keys = ds[woName].keys
|
|
26
99
|
|
|
27
100
|
//one
|
|
28
101
|
let one = false
|
|
@@ -99,6 +172,17 @@ function adjustData(woName, userId, data, mode = 'save') { //mode: 'insert', 'sa
|
|
|
99
172
|
})
|
|
100
173
|
}
|
|
101
174
|
|
|
175
|
+
//id, 未提供有效主鍵者由本套件補值, 令回傳之主鍵與實際寫入者一致, 補值器同各ORM之genIDSeq
|
|
176
|
+
//有效主鍵之判定同w-orm系列: 非空字串或數字(reladb、mdb視數字為有效)
|
|
177
|
+
//補值寫在上方之複本, 不改呼叫端原物件; 無欄位之列不補, 保留ORM視空物件為無效輸入之行為
|
|
178
|
+
if (isPkIdString(ds[woName], keys)) {
|
|
179
|
+
each(data, (v) => {
|
|
180
|
+
if (size(v) > 0 && !isestr(v.id) && !isnum(v.id)) {
|
|
181
|
+
v.id = genIDSeq()
|
|
182
|
+
}
|
|
183
|
+
})
|
|
184
|
+
}
|
|
185
|
+
|
|
102
186
|
//復原data型態, 若原本data為物件則取第0個回傳
|
|
103
187
|
data = one ? data[0] : data
|
|
104
188
|
|
|
@@ -106,22 +190,22 @@ function adjustData(woName, userId, data, mode = 'save') { //mode: 'insert', 'sa
|
|
|
106
190
|
}
|
|
107
191
|
|
|
108
192
|
|
|
109
|
-
async function mapOrm(userId, woName, mode, input) {
|
|
193
|
+
async function mapOrm(ds, woItems, userId, woName, mode, input) {
|
|
110
194
|
//由woName自動綁定至woItems內的ORM物件
|
|
111
195
|
let err = null
|
|
112
196
|
let r
|
|
113
197
|
|
|
114
198
|
//check
|
|
115
|
-
if (!iseobj(
|
|
116
|
-
return Promise.reject(
|
|
199
|
+
if (!iseobj(woItems)) {
|
|
200
|
+
return Promise.reject(`invalid woItems`)
|
|
117
201
|
}
|
|
118
202
|
|
|
119
203
|
//wo
|
|
120
|
-
let wo = get(
|
|
204
|
+
let wo = get(woItems, woName, null)
|
|
121
205
|
|
|
122
206
|
//check
|
|
123
207
|
if (wo === null) {
|
|
124
|
-
return Promise.reject(`
|
|
208
|
+
return Promise.reject(`can not find table[${woName}] in ORM`)
|
|
125
209
|
}
|
|
126
210
|
|
|
127
211
|
if (mode === 'select') {
|
|
@@ -130,7 +214,7 @@ async function mapOrm(userId, woName, mode, input) {
|
|
|
130
214
|
r = await wo.select(input)
|
|
131
215
|
.catch((msg) => {
|
|
132
216
|
console.log(`${woName}.select catch`, msg)
|
|
133
|
-
err = '
|
|
217
|
+
err = 'failed to select data'
|
|
134
218
|
})
|
|
135
219
|
|
|
136
220
|
//check
|
|
@@ -141,13 +225,23 @@ async function mapOrm(userId, woName, mode, input) {
|
|
|
141
225
|
}
|
|
142
226
|
else if (mode === 'insert' || mode === 'save') {
|
|
143
227
|
|
|
228
|
+
//inputOrig, 呼叫端原輸入, 逐筆失敗之訊息以呼叫端視角列出(本套件補出之主鍵只寫於複本, reject時呼叫端未曾取得)
|
|
229
|
+
let inputOrig = input
|
|
230
|
+
|
|
231
|
+
//check, 主鍵會被clearXSS改寫者整批拒絕, 不寫入任何一筆
|
|
232
|
+
let failsIdXSS = getItemsIdXSS(input)
|
|
233
|
+
if (size(failsIdXSS) > 0) {
|
|
234
|
+
console.log(`${woName}.${mode} id changed by clearXSS`, failsIdXSS)
|
|
235
|
+
return Promise.reject(getMsgIdXSS(mode === 'insert' ? 'failed to insert data' : 'failed to save data', failsIdXSS))
|
|
236
|
+
}
|
|
237
|
+
|
|
144
238
|
//adjustData
|
|
145
239
|
try {
|
|
146
|
-
input = adjustData(woName, userId, input, mode)
|
|
240
|
+
input = adjustData(ds, woName, userId, input, mode)
|
|
147
241
|
}
|
|
148
242
|
catch (e) {
|
|
149
|
-
console.log(
|
|
150
|
-
err = '
|
|
243
|
+
console.log(`${woName}.adjustData catch`, e.toString())
|
|
244
|
+
err = 'failed to adjust data'
|
|
151
245
|
return Promise.reject(err)
|
|
152
246
|
}
|
|
153
247
|
|
|
@@ -155,15 +249,15 @@ async function mapOrm(userId, woName, mode, input) {
|
|
|
155
249
|
if (mode === 'insert') {
|
|
156
250
|
r = await wo.insert(input)
|
|
157
251
|
.catch((msg) => {
|
|
158
|
-
console.log(`${woName}.
|
|
159
|
-
err = '
|
|
252
|
+
console.log(`${woName}.insert catch`, msg)
|
|
253
|
+
err = 'failed to insert data'
|
|
160
254
|
})
|
|
161
255
|
}
|
|
162
256
|
else if (mode === 'save') {
|
|
163
257
|
r = await wo.save(input)
|
|
164
258
|
.catch((msg) => {
|
|
165
259
|
console.log(`${woName}.save catch`, msg)
|
|
166
|
-
err = '
|
|
260
|
+
err = 'failed to save data'
|
|
167
261
|
})
|
|
168
262
|
}
|
|
169
263
|
|
|
@@ -172,6 +266,16 @@ async function mapOrm(userId, woName, mode, input) {
|
|
|
172
266
|
return Promise.reject(err)
|
|
173
267
|
}
|
|
174
268
|
|
|
269
|
+
//check, save單筆失敗時整批仍resolve並以該筆ok為0回報, 須逐筆檢查
|
|
270
|
+
//insert之錯誤皆屬整批性而reject, 其回傳為聚合物件而非逐筆陣列, 故不須檢查
|
|
271
|
+
if (mode === 'save') {
|
|
272
|
+
let fails = getItemsFail(r, input)
|
|
273
|
+
if (size(fails) > 0) {
|
|
274
|
+
console.log(`${woName}.save fail`, fails)
|
|
275
|
+
return Promise.reject(getMsgFail('failed to save data', getItemsFail(r, inputOrig)))
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
|
|
175
279
|
//僅回傳id避免大量數據時造成負擔
|
|
176
280
|
if (isarr(input)) {
|
|
177
281
|
r = map(input, (v) => {
|
|
@@ -190,9 +294,18 @@ async function mapOrm(userId, woName, mode, input) {
|
|
|
190
294
|
r = await wo.del(input)
|
|
191
295
|
.catch((msg) => {
|
|
192
296
|
console.log(`${woName}.del catch`, msg)
|
|
193
|
-
err = '
|
|
297
|
+
err = 'failed to delete data'
|
|
194
298
|
})
|
|
195
299
|
|
|
300
|
+
//check, 單筆失敗時整批仍resolve並以該筆ok為0回報, 須逐筆檢查
|
|
301
|
+
if (!isestr(err)) {
|
|
302
|
+
let fails = getItemsFail(r, input)
|
|
303
|
+
if (size(fails) > 0) {
|
|
304
|
+
console.log(`${woName}.del fail`, fails)
|
|
305
|
+
err = getMsgFail('failed to delete data', fails)
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
|
|
196
309
|
}
|
|
197
310
|
else if (mode === 'delAll') {
|
|
198
311
|
|
|
@@ -200,7 +313,7 @@ async function mapOrm(userId, woName, mode, input) {
|
|
|
200
313
|
r = await wo.delAll(input)
|
|
201
314
|
.catch((msg) => {
|
|
202
315
|
console.log(`${woName}.delAll catch`, msg)
|
|
203
|
-
err = '
|
|
316
|
+
err = 'failed to delete data'
|
|
204
317
|
})
|
|
205
318
|
|
|
206
319
|
}
|
|
@@ -208,31 +321,47 @@ async function mapOrm(userId, woName, mode, input) {
|
|
|
208
321
|
|
|
209
322
|
//check
|
|
210
323
|
if (!iseobj(input)) {
|
|
211
|
-
return Promise.reject('mix
|
|
324
|
+
return Promise.reject('invalid input of mix, need the result object of ltdtDiffByKey')
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
//check, 新增與修改之主鍵會被clearXSS改寫者, 於任何步驟寫入之前整批拒絕
|
|
328
|
+
let msgsIdXSS = []
|
|
329
|
+
for (let [key, msg] of [['add', 'failed to insert data'], ['diff', 'failed to update data']]) {
|
|
330
|
+
let fails = getItemsIdXSS(get(input, key, []))
|
|
331
|
+
if (size(fails) > 0) {
|
|
332
|
+
console.log(`${woName}.mix ${key} id changed by clearXSS`, fails)
|
|
333
|
+
msgsIdXSS.push(getMsgIdXSS(msg, fails))
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
if (size(msgsIdXSS) > 0) {
|
|
337
|
+
return Promise.reject(join(msgsIdXSS, '; '))
|
|
212
338
|
}
|
|
213
339
|
|
|
214
340
|
//rt,r
|
|
215
341
|
let rt
|
|
216
342
|
r = [] //default to array
|
|
217
343
|
|
|
344
|
+
//msgsFail, 逐筆失敗僅影響該筆, 不中斷後續步驟, 待全部步驟完成後一併reject; 整批性錯誤則仍立即reject
|
|
345
|
+
let msgsFail = []
|
|
346
|
+
|
|
218
347
|
let add = get(input, 'add', [])
|
|
219
348
|
if (size(add) > 0) {
|
|
220
349
|
|
|
221
350
|
//adjustData
|
|
222
351
|
try {
|
|
223
|
-
add = adjustData(woName, userId, add, 'insert+save')
|
|
352
|
+
add = adjustData(ds, woName, userId, add, 'insert+save')
|
|
224
353
|
}
|
|
225
354
|
catch (e) {
|
|
226
|
-
console.log(
|
|
227
|
-
err = '
|
|
355
|
+
console.log(`${woName}.adjustData catch`, e.toString())
|
|
356
|
+
err = 'failed to adjust data'
|
|
228
357
|
return Promise.reject(err)
|
|
229
358
|
}
|
|
230
359
|
|
|
231
360
|
//insert
|
|
232
361
|
await wo.insert(add)
|
|
233
362
|
.catch((msg) => {
|
|
234
|
-
console.log(`${woName}.
|
|
235
|
-
err = '
|
|
363
|
+
console.log(`${woName}.insert catch`, msg)
|
|
364
|
+
err = 'failed to insert data'
|
|
236
365
|
})
|
|
237
366
|
|
|
238
367
|
//check
|
|
@@ -251,10 +380,10 @@ async function mapOrm(userId, woName, mode, input) {
|
|
|
251
380
|
if (size(del) > 0) {
|
|
252
381
|
|
|
253
382
|
//del
|
|
254
|
-
await wo.del(del)
|
|
383
|
+
let rs = await wo.del(del)
|
|
255
384
|
.catch((msg) => {
|
|
256
|
-
console.log(`${woName}.
|
|
257
|
-
err = '
|
|
385
|
+
console.log(`${woName}.del catch`, msg)
|
|
386
|
+
err = 'failed to delete data'
|
|
258
387
|
})
|
|
259
388
|
|
|
260
389
|
//check
|
|
@@ -262,6 +391,13 @@ async function mapOrm(userId, woName, mode, input) {
|
|
|
262
391
|
return Promise.reject(err)
|
|
263
392
|
}
|
|
264
393
|
|
|
394
|
+
//check, 單筆失敗時整批仍resolve並以該筆ok為0回報, 須逐筆檢查
|
|
395
|
+
let fails = getItemsFail(rs, del)
|
|
396
|
+
if (size(fails) > 0) {
|
|
397
|
+
console.log(`${woName}.del fail`, fails)
|
|
398
|
+
msgsFail.push(getMsgFail('failed to delete data', fails))
|
|
399
|
+
}
|
|
400
|
+
|
|
265
401
|
//r
|
|
266
402
|
rt = map(del, (v) => {
|
|
267
403
|
return { id: v.id } //僅回傳id避免大量數據時造成負擔
|
|
@@ -272,21 +408,24 @@ async function mapOrm(userId, woName, mode, input) {
|
|
|
272
408
|
let diff = get(input, 'diff', [])
|
|
273
409
|
if (size(diff) > 0) {
|
|
274
410
|
|
|
411
|
+
//diffOrig, 呼叫端原輸入, 逐筆失敗之訊息以呼叫端視角列出
|
|
412
|
+
let diffOrig = diff
|
|
413
|
+
|
|
275
414
|
//adjustData
|
|
276
415
|
try {
|
|
277
|
-
diff = adjustData(woName, userId, diff)
|
|
416
|
+
diff = adjustData(ds, woName, userId, diff)
|
|
278
417
|
}
|
|
279
418
|
catch (e) {
|
|
280
|
-
console.log(
|
|
281
|
-
err = '
|
|
419
|
+
console.log(`${woName}.adjustData catch`, e.toString())
|
|
420
|
+
err = 'failed to adjust data'
|
|
282
421
|
return Promise.reject(err)
|
|
283
422
|
}
|
|
284
423
|
|
|
285
424
|
//save
|
|
286
|
-
await wo.save(diff)
|
|
425
|
+
let rs = await wo.save(diff)
|
|
287
426
|
.catch((msg) => {
|
|
288
427
|
console.log(`${woName}.save catch`, msg)
|
|
289
|
-
err = '
|
|
428
|
+
err = 'failed to update data'
|
|
290
429
|
})
|
|
291
430
|
|
|
292
431
|
//check
|
|
@@ -294,6 +433,13 @@ async function mapOrm(userId, woName, mode, input) {
|
|
|
294
433
|
return Promise.reject(err)
|
|
295
434
|
}
|
|
296
435
|
|
|
436
|
+
//check, 單筆失敗時整批仍resolve並以該筆ok為0回報, 須逐筆檢查
|
|
437
|
+
let fails = getItemsFail(rs, diff)
|
|
438
|
+
if (size(fails) > 0) {
|
|
439
|
+
console.log(`${woName}.save fail`, fails)
|
|
440
|
+
msgsFail.push(getMsgFail('failed to update data', getItemsFail(rs, diffOrig)))
|
|
441
|
+
}
|
|
442
|
+
|
|
297
443
|
//r
|
|
298
444
|
rt = map(diff, (v) => {
|
|
299
445
|
return { id: v.id } //僅回傳id避免大量數據時造成負擔
|
|
@@ -301,6 +447,11 @@ async function mapOrm(userId, woName, mode, input) {
|
|
|
301
447
|
r = [...r, ...rt]
|
|
302
448
|
}
|
|
303
449
|
|
|
450
|
+
//check, 各步驟之逐筆失敗於全部步驟完成後一併reject
|
|
451
|
+
if (size(msgsFail) > 0) {
|
|
452
|
+
return Promise.reject(join(msgsFail, '; '))
|
|
453
|
+
}
|
|
454
|
+
|
|
304
455
|
//console.log('mix r', r)
|
|
305
456
|
}
|
|
306
457
|
else {
|
|
@@ -317,9 +468,10 @@ async function mapOrm(userId, woName, mode, input) {
|
|
|
317
468
|
|
|
318
469
|
|
|
319
470
|
function getMapOrm(ds, woItems) {
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
471
|
+
//每次呼叫各自綁定ds與woItems並回傳新函數, 令同一行程內之多個實例互不干擾; 回傳函數之參數形狀維持(userId, woName, mode, input)
|
|
472
|
+
return async (userId, woName, mode, input) => {
|
|
473
|
+
return mapOrm(ds, woItems, userId, woName, mode, input)
|
|
474
|
+
}
|
|
323
475
|
}
|
|
324
476
|
|
|
325
477
|
|
package/src/getProc.mjs
CHANGED
|
@@ -1,88 +1,72 @@
|
|
|
1
1
|
import get from 'lodash-es/get.js'
|
|
2
|
-
import size from 'lodash-es/size.js'
|
|
3
2
|
import filter from 'lodash-es/filter.js'
|
|
4
3
|
import isarr from 'wsemi/src/isarr.mjs'
|
|
5
4
|
import isestr from 'wsemi/src/isestr.mjs'
|
|
5
|
+
import iseobj from 'wsemi/src/iseobj.mjs'
|
|
6
6
|
import haskey from 'wsemi/src/haskey.mjs'
|
|
7
7
|
import pmSeries from 'wsemi/src/pmSeries.mjs'
|
|
8
|
-
import pmChain from 'wsemi/src/pmChain.mjs'
|
|
9
8
|
import isfun from 'wsemi/src/isfun.mjs'
|
|
10
9
|
import isbol from 'wsemi/src/isbol.mjs'
|
|
11
10
|
|
|
12
11
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
let _getUserById = null
|
|
18
|
-
let useCheckUser = true
|
|
19
|
-
let useExcludeWhenNotAdmin = true
|
|
12
|
+
function addFun(fs, f, name, useGuard = false) {
|
|
13
|
+
//由外部添加擴充函數至該實例之陣列
|
|
14
|
+
//延後1ms生效係沿用既有之生效時序: 原為避開模組層陣列於循環引用時之「Cannot access before initialization」,
|
|
15
|
+
//改為各實例狀態後該原因已不成立, 惟使用端之啟動流程可能依賴此時序, 故保留
|
|
20
16
|
|
|
17
|
+
//check, 非函數於註冊時即拋錯; useGuard(check用)另推入恆reject之守門函數, 令拋錯被呼叫端攔下時該實例仍維持fail-closed
|
|
18
|
+
//註冊時拋錯屬建構期錯誤故為Error; 守門函數於procOrm內reject, 與procOrm其餘拒絕一致為字串
|
|
19
|
+
if (!isfun(f)) {
|
|
20
|
+
let msg = `${name} needs a function, but got ${typeof f}`
|
|
21
|
+
if (useGuard) {
|
|
22
|
+
fs.push(() => {
|
|
23
|
+
return Promise.reject(msg)
|
|
24
|
+
})
|
|
25
|
+
}
|
|
26
|
+
throw new Error(msg)
|
|
27
|
+
}
|
|
21
28
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
setTimeout(() => { //因互相引用的關係會出現「Cannot access '_funChecks' before initialization」, 故通過timer脫勾成為動態引用, 避開執行mInitialTestData時會先編譯與偵測問題
|
|
26
|
-
_funChecks.push(f)
|
|
27
|
-
}, 1)
|
|
28
|
-
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
function addFunPreProcessing(f) {
|
|
33
|
-
//由外部添加擴充前處理函數
|
|
34
|
-
|
|
35
|
-
setTimeout(() => { //因互相引用的關係會出現「Cannot access '_funChecks' before initialization」, 故通過timer脫勾成為動態引用, 避開執行mInitialTestData時會先編譯與偵測問題
|
|
36
|
-
_funPreProcessing.push(f)
|
|
37
|
-
}, 1)
|
|
38
|
-
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
function addFunPostProcessing(f) {
|
|
43
|
-
//由外部添加擴充後置處理函數
|
|
44
|
-
|
|
45
|
-
setTimeout(() => { //因互相引用的關係會出現「Cannot access '_funChecks' before initialization」, 故通過timer脫勾成為動態引用, 避開執行mInitialTestData時會先編譯與偵測問題
|
|
46
|
-
_funPostProcessing.push(f)
|
|
29
|
+
setTimeout(() => {
|
|
30
|
+
fs.push(f)
|
|
47
31
|
}, 1)
|
|
48
32
|
|
|
49
33
|
}
|
|
50
34
|
|
|
51
35
|
|
|
52
|
-
async function procOrm(userId, woName, mode, input) {
|
|
53
|
-
//封裝mapOrm, 添加檢查userId
|
|
36
|
+
async function procOrm(st, userId, woName, mode, input) {
|
|
37
|
+
//封裝mapOrm, 添加檢查userId功能, st為該實例之狀態
|
|
54
38
|
|
|
55
39
|
//check
|
|
56
|
-
if (!isfun(
|
|
40
|
+
if (!isfun(st.mapOrm)) {
|
|
57
41
|
return Promise.reject(`invalid mapOrm`)
|
|
58
42
|
}
|
|
59
43
|
|
|
60
44
|
//check
|
|
61
|
-
if (useCheckUser && !isestr(userId)) {
|
|
45
|
+
if (st.useCheckUser && !isestr(userId)) {
|
|
62
46
|
console.log('userId', userId)
|
|
63
|
-
console.log('
|
|
64
|
-
return Promise.reject(
|
|
47
|
+
console.log('invalid userId')
|
|
48
|
+
return Promise.reject(`invalid userId`)
|
|
65
49
|
}
|
|
66
50
|
|
|
67
51
|
//isAdmin
|
|
68
52
|
let isAdmin = 'n'
|
|
69
53
|
|
|
70
54
|
//useCheckUser
|
|
71
|
-
if (useCheckUser) {
|
|
55
|
+
if (st.useCheckUser) {
|
|
72
56
|
|
|
73
57
|
//check
|
|
74
|
-
if (!isfun(
|
|
58
|
+
if (!isfun(st.getUserById)) {
|
|
75
59
|
return Promise.reject(`invalid getUserById`)
|
|
76
60
|
}
|
|
77
61
|
|
|
78
62
|
//oSelf
|
|
79
|
-
let oSelf = await
|
|
63
|
+
let oSelf = await st.getUserById(userId)
|
|
80
64
|
|
|
81
65
|
//check
|
|
82
66
|
if (!oSelf) {
|
|
83
67
|
console.log('userId', userId)
|
|
84
|
-
console.log('
|
|
85
|
-
return Promise.reject(
|
|
68
|
+
console.log('can not find user')
|
|
69
|
+
return Promise.reject(`can not find user`)
|
|
86
70
|
}
|
|
87
71
|
|
|
88
72
|
//isAdmin
|
|
@@ -101,29 +85,33 @@ async function procOrm(userId, woName, mode, input) {
|
|
|
101
85
|
if (isActive !== 'y') {
|
|
102
86
|
console.log('userId', userId)
|
|
103
87
|
console.log('oSelf', oSelf)
|
|
104
|
-
console.log('
|
|
105
|
-
return Promise.reject(
|
|
88
|
+
console.log('user is inactive or invalid')
|
|
89
|
+
return Promise.reject(`user is inactive or invalid`)
|
|
106
90
|
}
|
|
107
91
|
|
|
108
92
|
}
|
|
109
93
|
|
|
110
|
-
//
|
|
111
|
-
await pmSeries(
|
|
94
|
+
//funChecks, 擴充檢查, 提供給各 mOrm[OOO].mjs 檢查
|
|
95
|
+
await pmSeries(st.funChecks, (v) => {
|
|
112
96
|
return v({ woName, userId, isAdmin, mode, input })
|
|
113
97
|
})
|
|
114
98
|
|
|
115
|
-
//
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
99
|
+
//funPreProcessing, 擴充前處理input, 提供給各 mOrm[OOO].mjs 前處理
|
|
100
|
+
//逐一呼叫, 各前處理皆收新建之參數物件, input為前一個回傳之input; 回傳須為含input之物件, 否則reject, 避免input成為undefined而令select回全表
|
|
101
|
+
//迭代前先取快照, 呼叫進行中註冊者不套用於本次呼叫
|
|
102
|
+
for (let f of [...st.funPreProcessing]) {
|
|
103
|
+
let paramsOut = await f({ woName, userId, isAdmin, mode, input })
|
|
104
|
+
if (!iseobj(paramsOut) || paramsOut.input === undefined) {
|
|
105
|
+
return Promise.reject(`invalid return of pre-processing, need an object with input`)
|
|
106
|
+
}
|
|
107
|
+
input = paramsOut.input
|
|
120
108
|
}
|
|
121
109
|
|
|
122
110
|
//output
|
|
123
|
-
let output = await
|
|
111
|
+
let output = await st.mapOrm(userId, woName, mode, input)
|
|
124
112
|
|
|
125
113
|
//useExcludeWhenNotAdmin, 非管理者則要過濾掉已刪除之數據
|
|
126
|
-
if (useExcludeWhenNotAdmin && isAdmin === 'n') {
|
|
114
|
+
if (st.useExcludeWhenNotAdmin && isAdmin === 'n') {
|
|
127
115
|
if (mode === 'select' && isarr(output)) { //有可能為錯誤物件
|
|
128
116
|
output = filter(output, (v) => {
|
|
129
117
|
if (haskey(v, 'isActive')) { //針對有isActive屬性
|
|
@@ -134,9 +122,10 @@ async function procOrm(userId, woName, mode, input) {
|
|
|
134
122
|
}
|
|
135
123
|
}
|
|
136
124
|
|
|
137
|
-
//
|
|
138
|
-
|
|
139
|
-
|
|
125
|
+
//funPostProcessing, 擴充後處理output, 提供給各 mOrm[OOO].mjs 後處理
|
|
126
|
+
//逐一呼叫, 各後處理皆收新建之參數物件, output為前一個之回傳值, 回傳值成為新output; 迭代前先取快照
|
|
127
|
+
for (let f of [...st.funPostProcessing]) {
|
|
128
|
+
output = await f({ woName, userId, isAdmin, mode, input, output })
|
|
140
129
|
}
|
|
141
130
|
|
|
142
131
|
return output
|
|
@@ -150,29 +139,49 @@ function getProc(mapOrm, opt = {}) {
|
|
|
150
139
|
return Promise.reject(`invalid mapOrm`)
|
|
151
140
|
}
|
|
152
141
|
|
|
153
|
-
//save _mapOrm
|
|
154
|
-
_mapOrm = mapOrm
|
|
155
|
-
|
|
156
142
|
//useCheckUser
|
|
157
|
-
useCheckUser = get(opt, 'useCheckUser')
|
|
143
|
+
let useCheckUser = get(opt, 'useCheckUser')
|
|
158
144
|
if (!isbol(useCheckUser)) {
|
|
159
145
|
useCheckUser = true
|
|
160
146
|
}
|
|
161
147
|
|
|
162
|
-
//
|
|
163
|
-
|
|
148
|
+
//getUserById, 不事先檢查, 因若useCheckUser=false可允許不給
|
|
149
|
+
let getUserById = get(opt, 'getUserById')
|
|
164
150
|
|
|
165
151
|
//useExcludeWhenNotAdmin
|
|
166
|
-
useExcludeWhenNotAdmin = get(opt, 'useExcludeWhenNotAdmin')
|
|
152
|
+
let useExcludeWhenNotAdmin = get(opt, 'useExcludeWhenNotAdmin')
|
|
167
153
|
if (!isbol(useExcludeWhenNotAdmin)) {
|
|
168
154
|
useExcludeWhenNotAdmin = true
|
|
169
155
|
}
|
|
170
156
|
|
|
157
|
+
//st, 各實例之狀態, 每次呼叫各自建立, 令同一行程內之多個實例互不干擾
|
|
158
|
+
let st = {
|
|
159
|
+
funChecks: [],
|
|
160
|
+
funPreProcessing: [],
|
|
161
|
+
funPostProcessing: [],
|
|
162
|
+
mapOrm,
|
|
163
|
+
getUserById,
|
|
164
|
+
useCheckUser,
|
|
165
|
+
useExcludeWhenNotAdmin,
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
//回傳函數綁定st而不依賴this, 使用端可解構取用並當裸函數傳遞
|
|
171
169
|
return {
|
|
172
|
-
addFunCheck
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
170
|
+
addFunCheck: (f) => {
|
|
171
|
+
//由外部添加擴充檢查函數
|
|
172
|
+
addFun(st.funChecks, f, 'addFunCheck', true)
|
|
173
|
+
},
|
|
174
|
+
addFunPreProcessing: (f) => {
|
|
175
|
+
//由外部添加擴充前處理函數
|
|
176
|
+
addFun(st.funPreProcessing, f, 'addFunPreProcessing')
|
|
177
|
+
},
|
|
178
|
+
addFunPostProcessing: (f) => {
|
|
179
|
+
//由外部添加擴充後置處理函數
|
|
180
|
+
addFun(st.funPostProcessing, f, 'addFunPostProcessing')
|
|
181
|
+
},
|
|
182
|
+
procOrm: async (userId, woName, mode, input) => {
|
|
183
|
+
return procOrm(st, userId, woName, mode, input)
|
|
184
|
+
},
|
|
176
185
|
}
|
|
177
186
|
}
|
|
178
187
|
|