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/src/getProc.mjs CHANGED
@@ -1,180 +1,104 @@
1
- import get from 'lodash-es/get.js'
2
- import size from 'lodash-es/size.js'
3
- import filter from 'lodash-es/filter.js'
4
- import isarr from 'wsemi/src/isarr.mjs'
5
- import isestr from 'wsemi/src/isestr.mjs'
6
- import haskey from 'wsemi/src/haskey.mjs'
7
- import pmSeries from 'wsemi/src/pmSeries.mjs'
8
- import pmChain from 'wsemi/src/pmChain.mjs'
9
- import isfun from 'wsemi/src/isfun.mjs'
10
- import isbol from 'wsemi/src/isbol.mjs'
11
-
12
-
13
- let _funChecks = []
14
- let _funPreProcessing = []
15
- let _funPostProcessing = []
16
- let _mapOrm = null
17
- let _getUserById = null
18
- let useCheckUser = true
19
- let useExcludeWhenNotAdmin = true
20
-
21
-
22
- function addFunCheck(f) {
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)
47
- }, 1)
48
-
49
- }
50
-
51
-
52
- async function procOrm(userId, woName, mode, input) {
53
- //封裝mapOrm, 添加檢查userId功能
54
-
55
- //check
56
- if (!isfun(_mapOrm)) {
57
- return Promise.reject(`invalid mapOrm`)
58
- }
59
-
60
- //check
61
- if (useCheckUser && !isestr(userId)) {
62
- console.log('userId', userId)
63
- console.log('找不到使用者主鍵')
64
- return Promise.reject(`找不到使用者主鍵`)
65
- }
66
-
67
- //isAdmin
68
- let isAdmin = 'n'
69
-
70
- //useCheckUser
71
- if (useCheckUser) {
72
-
73
- //check
74
- if (!isfun(_getUserById)) {
75
- return Promise.reject(`invalid getUserById`)
76
- }
77
-
78
- //oSelf
79
- let oSelf = await _getUserById(userId)
80
-
81
- //check
82
- if (!oSelf) {
83
- console.log('userId', userId)
84
- console.log('找不到使用者資訊')
85
- return Promise.reject(`找不到使用者資訊`)
86
- }
87
-
88
- //isAdmin
89
- isAdmin = get(oSelf, 'isAdmin')
90
- if (isAdmin !== 'y' && isAdmin !== 'n') {
91
- isAdmin = 'n'
92
- }
93
-
94
- //isActive
95
- let isActive = get(oSelf, 'isActive')
96
- if (isActive !== 'y' && isActive !== 'n') {
97
- isActive = 'n'
98
- }
99
-
100
- //check
101
- if (isActive !== 'y') {
102
- console.log('userId', userId)
103
- console.log('oSelf', oSelf)
104
- console.log('使用者被停權或無有效使用者資訊')
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
@@ -76,7 +76,7 @@ function getSettings(opt = {}) {
76
76
  }
77
77
  catch (err) {
78
78
  console.log(err)
79
- throw new Error('伺服器讀取設定檔失敗')
79
+ throw new Error('failed to read settings file')
80
80
  }
81
81
 
82
82
  return st
@@ -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, '刪除資料失敗: id[123]')
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, '刪除資料失敗: id[456]')
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&lt;b'].includes(v.id)), JSON.stringify(rs))
91
+ })
92
+
68
93
  })