w-serv-orm 1.1.7 → 1.1.9
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 +2 -6
- package/dist/w-serv-orm.umd.js +2 -2
- package/dist/w-serv-orm.umd.js.map +1 -1
- package/docs/WServOrm.html +21 -158
- package/docs/WServOrm.mjs.html +48 -16
- package/docs/index.html +1 -1
- package/g-mOrm.mjs +2 -6
- package/package.json +3 -3
- package/schema/tables/tests.mjs +1 -1
- package/src/WServOrm.mjs +46 -14
- package/src/genTestData.mjs +2 -2
- package/src/getDb.mjs +27 -6
- package/src/getDbBackupAndRecover.mjs +27 -2
- package/src/getItemsFail.mjs +2 -34
- package/src/getMapOrm.mjs +128 -46
- package/src/getProc.mjs +104 -180
- package/src/getSettings.mjs +1 -1
- package/test/api-procOrm.test.mjs +27 -2
- package/test/unit-WServOrm.test.mjs +567 -0
- package/test/unit-getDb.test.mjs +188 -0
- package/test/unit-mapOrm.test.mjs +119 -14
package/src/getProc.mjs
CHANGED
|
@@ -1,180 +1,104 @@
|
|
|
1
|
-
import
|
|
2
|
-
import
|
|
3
|
-
import
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
let
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
//
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
//
|
|
71
|
-
if (
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
return Promise.reject(`使用者被停權或無有效使用者資訊`)
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
//_funChecks, 擴充檢查, 提供給各 mOrm[OOO].mjs 檢查
|
|
111
|
-
await pmSeries(_funChecks, (v) => {
|
|
112
|
-
return v({ woName, userId, isAdmin, mode, input })
|
|
113
|
-
})
|
|
114
|
-
|
|
115
|
-
//_funPreProcessing, 擴充前處理input, 提供給各 mOrm[OOO].mjs 前處理
|
|
116
|
-
if (size(_funPreProcessing) > 0) {
|
|
117
|
-
let paramsIn = { woName, userId, isAdmin, mode, input } //將input與其他資訊一起封裝成params
|
|
118
|
-
let paramsOut = await pmChain(_funPreProcessing, paramsIn) //各非同步函數拿到輸入處理完皆輸出一樣結構數據params, 使各非同步函數都可拿到非input的重要資訊
|
|
119
|
-
input = paramsOut.input //由params提取input出來
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
//output
|
|
123
|
-
let output = await _mapOrm(userId, woName, mode, input)
|
|
124
|
-
|
|
125
|
-
//useExcludeWhenNotAdmin, 非管理者則要過濾掉已刪除之數據
|
|
126
|
-
if (useExcludeWhenNotAdmin && isAdmin === 'n') {
|
|
127
|
-
if (mode === 'select' && isarr(output)) { //有可能為錯誤物件
|
|
128
|
-
output = filter(output, (v) => {
|
|
129
|
-
if (haskey(v, 'isActive')) { //針對有isActive屬性
|
|
130
|
-
return !(v.isActive === 'n') //過濾isActive='n'
|
|
131
|
-
}
|
|
132
|
-
return true
|
|
133
|
-
})
|
|
134
|
-
}
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
//_funPostProcessing, 擴充後處理output, 提供給各 mOrm[OOO].mjs 後處理
|
|
138
|
-
if (size(_funPostProcessing) > 0) {
|
|
139
|
-
output = await pmChain(_funPostProcessing, { woName, userId, isAdmin, mode, input, output })
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
return output
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
function getProc(mapOrm, opt = {}) {
|
|
147
|
-
|
|
148
|
-
//check
|
|
149
|
-
if (!isfun(mapOrm)) {
|
|
150
|
-
return Promise.reject(`invalid mapOrm`)
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
//save _mapOrm
|
|
154
|
-
_mapOrm = mapOrm
|
|
155
|
-
|
|
156
|
-
//useCheckUser
|
|
157
|
-
useCheckUser = get(opt, 'useCheckUser')
|
|
158
|
-
if (!isbol(useCheckUser)) {
|
|
159
|
-
useCheckUser = true
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
//save _getUserById, 不事先檢查, 因若useCheckUser=false可允許不給
|
|
163
|
-
_getUserById = get(opt, 'getUserById')
|
|
164
|
-
|
|
165
|
-
//useExcludeWhenNotAdmin
|
|
166
|
-
useExcludeWhenNotAdmin = get(opt, 'useExcludeWhenNotAdmin')
|
|
167
|
-
if (!isbol(useExcludeWhenNotAdmin)) {
|
|
168
|
-
useExcludeWhenNotAdmin = true
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
return {
|
|
172
|
-
addFunCheck,
|
|
173
|
-
addFunPreProcessing,
|
|
174
|
-
addFunPostProcessing,
|
|
175
|
-
procOrm,
|
|
176
|
-
}
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
export default getProc
|
|
1
|
+
import iseobj from 'wsemi/src/iseobj.mjs'
|
|
2
|
+
import pmSeries from 'wsemi/src/pmSeries.mjs'
|
|
3
|
+
import isfun from 'wsemi/src/isfun.mjs'
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
function addFun(fs, f, name, useGuard = false) {
|
|
7
|
+
//由外部添加擴充函數至該實例之陣列
|
|
8
|
+
//延後1ms生效係沿用既有之生效時序: 原為避開模組層陣列於循環引用時之「Cannot access before initialization」,
|
|
9
|
+
//改為各實例狀態後該原因已不成立, 惟使用端之啟動流程可能依賴此時序, 故保留
|
|
10
|
+
|
|
11
|
+
//check, 非函數於註冊時即拋錯; useGuard(check用)另推入恆reject之守門函數, 令拋錯被呼叫端攔下時該實例仍維持fail-closed
|
|
12
|
+
//註冊時拋錯屬建構期錯誤故為Error; 守門函數於procOrm內reject, 與procOrm其餘拒絕一致為字串
|
|
13
|
+
if (!isfun(f)) {
|
|
14
|
+
let msg = `${name} needs a function, but got ${typeof f}`
|
|
15
|
+
if (useGuard) {
|
|
16
|
+
fs.push(() => {
|
|
17
|
+
return Promise.reject(msg)
|
|
18
|
+
})
|
|
19
|
+
}
|
|
20
|
+
throw new Error(msg)
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
setTimeout(() => {
|
|
24
|
+
fs.push(f)
|
|
25
|
+
}, 1)
|
|
26
|
+
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
async function procOrm(st, userId, woName, mode, input) {
|
|
31
|
+
//封裝mapOrm, 依序執行擴充檢查、前處理、ORM操作與後處理, st為該實例之狀態
|
|
32
|
+
//不做授權: 不檢查使用者亦不依使用者過濾數據, userId只供寫入稽核欄位與交給擴充函數; 開放給前端時須由開放處負責授權
|
|
33
|
+
|
|
34
|
+
//check
|
|
35
|
+
if (!isfun(st.mapOrm)) {
|
|
36
|
+
return Promise.reject(`invalid mapOrm`)
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
//funChecks, 擴充檢查, 提供給各 mOrm[OOO].mjs 檢查
|
|
40
|
+
await pmSeries(st.funChecks, (v) => {
|
|
41
|
+
return v({ woName, userId, mode, input })
|
|
42
|
+
})
|
|
43
|
+
|
|
44
|
+
//funPreProcessing, 擴充前處理input, 提供給各 mOrm[OOO].mjs 前處理
|
|
45
|
+
//逐一呼叫, 各前處理皆收新建之參數物件, input為前一個回傳之input; 回傳須為含input之物件, 否則reject, 避免input成為undefined而令select回全表
|
|
46
|
+
//迭代前先取快照, 呼叫進行中註冊者不套用於本次呼叫
|
|
47
|
+
for (let f of [...st.funPreProcessing]) {
|
|
48
|
+
let paramsOut = await f({ woName, userId, mode, input })
|
|
49
|
+
if (!iseobj(paramsOut) || paramsOut.input === undefined) {
|
|
50
|
+
return Promise.reject(`invalid return of pre-processing, need an object with input`)
|
|
51
|
+
}
|
|
52
|
+
input = paramsOut.input
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
//output
|
|
56
|
+
let output = await st.mapOrm(userId, woName, mode, input)
|
|
57
|
+
|
|
58
|
+
//funPostProcessing, 擴充後處理output, 提供給各 mOrm[OOO].mjs 後處理
|
|
59
|
+
//逐一呼叫, 各後處理皆收新建之參數物件, output為前一個之回傳值, 回傳值成為新output; 迭代前先取快照
|
|
60
|
+
for (let f of [...st.funPostProcessing]) {
|
|
61
|
+
output = await f({ woName, userId, mode, input, output })
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
return output
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
function getProc(mapOrm) {
|
|
69
|
+
|
|
70
|
+
//check
|
|
71
|
+
if (!isfun(mapOrm)) {
|
|
72
|
+
return Promise.reject(`invalid mapOrm`)
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
//st, 各實例之狀態, 每次呼叫各自建立, 令同一行程內之多個實例互不干擾
|
|
76
|
+
let st = {
|
|
77
|
+
funChecks: [],
|
|
78
|
+
funPreProcessing: [],
|
|
79
|
+
funPostProcessing: [],
|
|
80
|
+
mapOrm,
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
//回傳函數綁定st而不依賴this, 使用端可解構取用並當裸函數傳遞
|
|
84
|
+
return {
|
|
85
|
+
addFunCheck: (f) => {
|
|
86
|
+
//由外部添加擴充檢查函數
|
|
87
|
+
addFun(st.funChecks, f, 'addFunCheck', true)
|
|
88
|
+
},
|
|
89
|
+
addFunPreProcessing: (f) => {
|
|
90
|
+
//由外部添加擴充前處理函數
|
|
91
|
+
addFun(st.funPreProcessing, f, 'addFunPreProcessing')
|
|
92
|
+
},
|
|
93
|
+
addFunPostProcessing: (f) => {
|
|
94
|
+
//由外部添加擴充後置處理函數
|
|
95
|
+
addFun(st.funPostProcessing, f, 'addFunPostProcessing')
|
|
96
|
+
},
|
|
97
|
+
procOrm: async (userId, woName, mode, input) => {
|
|
98
|
+
return procOrm(st, userId, woName, mode, input)
|
|
99
|
+
},
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
export default getProc
|
package/src/getSettings.mjs
CHANGED
|
@@ -46,7 +46,7 @@ describe('procOrm接w-orm-lowdb', function() {
|
|
|
46
46
|
it('A02 del含無效主鍵', async function() {
|
|
47
47
|
//R02: 逐筆ok為0即reject並列出失敗主鍵; R10: 單筆失敗不中斷整批, 有效筆已刪除
|
|
48
48
|
let err = await getReject(proc('del', [{ id: 'a1' }, { id: 123 }]))
|
|
49
|
-
assert.strict.equal(err, '
|
|
49
|
+
assert.strict.equal(err, 'failed to delete data: id[123]')
|
|
50
50
|
let rs = await proc('select', { id: 'a1' })
|
|
51
51
|
assert.strict.equal(rs.length, 0)
|
|
52
52
|
})
|
|
@@ -54,7 +54,7 @@ describe('procOrm接w-orm-lowdb', function() {
|
|
|
54
54
|
it('A03 mix之del含無效主鍵', async function() {
|
|
55
55
|
//R03: del之逐筆失敗不中斷diff, diff已寫入, 最後reject列出del之失敗
|
|
56
56
|
let err = await getReject(proc('mix', { del: [{ id: 456 }], diff: [{ id: 'a2', isActive: 'y' }] }))
|
|
57
|
-
assert.strict.equal(err, '
|
|
57
|
+
assert.strict.equal(err, 'failed to delete data: id[456]')
|
|
58
58
|
let rs = await proc('select', { id: 'a2' })
|
|
59
59
|
assert.strict.equal(rs.length, 1)
|
|
60
60
|
})
|
|
@@ -65,4 +65,29 @@ describe('procOrm接w-orm-lowdb', function() {
|
|
|
65
65
|
assert.strict.deepEqual(r, [{ n: 0, nDeleted: 0, ok: 1 }])
|
|
66
66
|
})
|
|
67
67
|
|
|
68
|
+
it('A05 save未帶主鍵時回傳之id可於資料庫查得', async function() {
|
|
69
|
+
//D1: 本套件補值並回傳, 與實際寫入之主鍵一致
|
|
70
|
+
let r = await proc('save', [{ isActive: 'y' }])
|
|
71
|
+
assert.ok(typeof r[0].id === 'string' && r[0].id.length > 0)
|
|
72
|
+
let rs = await proc('select', { id: r[0].id })
|
|
73
|
+
assert.strict.equal(rs.length, 1)
|
|
74
|
+
})
|
|
75
|
+
|
|
76
|
+
it('A06 主鍵為數學式者可依原主鍵查詢與刪除', async function() {
|
|
77
|
+
//wsemi 1.9.13起數學式不被clearXSS改寫, 寫入之主鍵即呼叫端所持者(1.9.12時會存成a < b而查不到、刪不掉)
|
|
78
|
+
let r = await proc('save', [{ id: 'a < b', isActive: 'y' }])
|
|
79
|
+
assert.strict.deepEqual(r, [{ id: 'a < b' }])
|
|
80
|
+
let rs = await proc('select', { id: 'a < b' })
|
|
81
|
+
assert.strict.equal(rs.length, 1)
|
|
82
|
+
let rd = await proc('del', [{ id: 'a < b' }])
|
|
83
|
+
assert.strict.deepEqual(rd, [{ n: 1, nDeleted: 1, ok: 1 }])
|
|
84
|
+
})
|
|
85
|
+
|
|
86
|
+
it('A07 主鍵會被改寫者整批拒絕且未寫入任何一筆', async function() {
|
|
87
|
+
let err = await getReject(proc('save', [{ id: 'ok-2', isActive: 'y' }, { id: 'a<b', isActive: 'y' }]))
|
|
88
|
+
assert.strict.equal(err, 'failed to save data, id contains HTML markup: data[1]')
|
|
89
|
+
let rs = await proc('select', {})
|
|
90
|
+
assert.ok(!rs.some((v) => ['ok-2', 'a<b', 'a<b'].includes(v.id)), JSON.stringify(rs))
|
|
91
|
+
})
|
|
92
|
+
|
|
68
93
|
})
|