w-serv-orm 1.1.9 → 1.1.11
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/dist/w-serv-orm.umd.js +2 -2
- package/dist/w-serv-orm.umd.js.map +1 -1
- package/docs/WServOrm.html +10 -6
- package/docs/WServOrm.mjs.html +13 -28
- package/docs/index.html +1 -1
- package/package.json +3 -3
- package/src/WServOrm.mjs +11 -26
- package/src/checkOptRemoved.mjs +25 -0
- package/src/getMapOrm.mjs +233 -14
- package/src/getProc.mjs +9 -7
- package/test/api-pkcast.test.mjs +113 -0
- package/test/api-procOrm.test.mjs +8 -7
- package/test/unit-WServOrm.test.mjs +88 -45
- package/test/unit-mapOrm.test.mjs +9 -9
- package/test/unit-pkcast.test.mjs +325 -0
- package/src/getItemsFail.mjs +0 -5
package/docs/WServOrm.html
CHANGED
|
@@ -78,15 +78,19 @@
|
|
|
78
78
|
<p>每次呼叫各自建立資料表ORM、設定與擴充函數,同一行程內之多個實例互不干擾;同一url與db之資料庫資源能否並存,依所用ORM而定。</p>
|
|
79
79
|
<p>procOrm之save、del與mix,於ORM回傳之逐筆結果有任一筆ok為0(單筆失敗)時reject,訊息列出失敗筆之主鍵,如'failed to save data: id[b]'(超過100字元者截斷顯示;無有效主鍵者以data[k]列其於輸入內之位置,k自0起;超過10筆者只列前10筆並附總筆數),錯誤原文另由console.log輸出。
|
|
80
80
|
單筆失敗不中斷整批,故reject時其餘筆數已寫入。
|
|
81
|
-
mix之各步驟遇單筆失敗不中斷後續步驟,待全部步驟完成後一併reject;遇整批性錯誤則立即reject
|
|
82
|
-
<p>insert、save與mix之新增、修改,數據未帶有效主鍵(
|
|
83
|
-
|
|
81
|
+
mix之各步驟遇單筆失敗不中斷後續步驟,待全部步驟完成後一併reject;遇整批性錯誤則立即reject,後續步驟不執行。mix於resolve時回傳各步驟所處理之列之id(依add、del、diff之序串接,del之列不分是否命中)。</p>
|
|
82
|
+
<p>insert、save與mix之新增、修改,數據未帶有效主鍵(未給、null、空字串,或非字串非數字之值,含NaN與±Infinity)者,若資料表之主鍵欄為id且為字串型(或型別未知;型別如STRING(36)者以括號前之字判定),由本套件產生主鍵(同各ORM之genIDSeq)並於resolve時回傳;單一空物件與只含一個空物件之陣列不補(ORM視為無效輸入而不寫入),多筆批次中之空物件列則補。
|
|
83
|
+
主鍵欄為字串型(或型別未知)之資料表,主鍵為有限數字者一律以其字串形式處置(如123視為'123'):寫入、resolve回傳、del與mix之刪除,以及select、delAll條件中主鍵之比較($eq、$ne、$gt、$gte、$lt、$lte、$in、$nin、$all,含$not、$and、$or、$nor之內)皆同,故以數字或其字串形式寫入、查詢與刪除者為同一筆,範圍比較依字串之字典序;主鍵欄為整數型者不轉型。轉型只作用於procOrm,經woItems直接操作者不轉型。舊版w-orm-mongodb(2025-04-29以前)保留之數字主鍵資料,以數字查詢將查不到。
|
|
84
|
+
reject時不回傳補出之主鍵,故重送未帶主鍵之數據會再產生新主鍵而重複寫入,需冪等重送者請自帶主鍵(非空字串或有限數字);save帶主鍵者重送以主鍵覆寫,del重送時已刪除者為主鍵未命中。
|
|
85
|
+
回傳之id為本套件依輸入(含補值與轉型)所得之主鍵,非ORM之寫入結果;ORM之比對規則較寬者(如w-orm-mdb之Jet不分大小寫、忽略尾端空白),insert略過或save命中之對象可能與回傳之id不同。lmdb、lowdb、level、mongodb等主鍵固定為字串id之ORM,其資料表之id須為字串型pk或不標型別;本套件以id為主鍵,主鍵欄非id之ORM(如w-orm-postgresql預設為time)之回傳id不具主鍵意義。
|
|
86
|
+
輸入之形狀:insert與save之各筆、mix之新增與修改之各筆須為物件,mix之add、del、diff若給予須為陣列,delAll之條件若給予須為物件,否則於任何寫入之前reject,如'failed to insert data, not an object: data[0]'、'invalid find of delAll, need an object'。
|
|
87
|
+
mix之add與del、del與diff含同一主鍵(依上述轉型後)者,於任何步驟之前整批reject且不寫入任何一筆,訊息列其位置而不回顯主鍵,如'invalid input of mix, same id in add and del: add[0], del[1]';因mix依add、del、diff之序執行,同鍵將使新增因已存在而略過後即被刪除。
|
|
84
88
|
insert(含mix之新增)為主鍵已存在即略過之冪等新增,回傳之id含被略過者;recover亦以insert還原,故資料表須先清空。
|
|
85
89
|
insert、save與mix之新增、修改,寫入前各欄位之字串皆經wsemi之clearXSS清除(白名單外之標籤轉義,白名單內之標籤刪除白名單外之屬性與危險網址,標籤外之文字只轉義會開始標籤之<,故'a < b'等數學式不改寫);select與del不經清除。
|
|
86
90
|
字串主鍵若會被清除改寫(含HTML標記,如<後緊接英文字母),整批reject且不寫入任何一筆(mix於任何步驟之前檢查新增與修改),訊息列其位置而不回顯主鍵,如'failed to save data, id contains HTML markup: data[1]';否則改寫後之主鍵與呼叫端所持者不同,以原主鍵查詢與刪除皆找不到。</p>
|
|
87
91
|
<p>擴充函數(addFunCheck、addFunPreProcessing、addFunPostProcessing)屬於各實例,重建實例後須重新註冊;註冊時須傳入函數,否則立即拋錯(addFunCheck另令該實例之procOrm一律reject);註冊後約1毫秒(以計時器)才生效,在此之前之procOrm呼叫不會套用。
|
|
88
92
|
呼叫契約:check以reject或拋錯阻擋,回傳值不影響結果,且早於前處理執行;前處理逐一呼叫,各收新建之參數物件{woName,userId,mode,input},input為前一個回傳之input,回傳須為含input之物件否則reject;後處理逐一呼叫,各收新建之參數物件且其output為前一個之回傳值,最後之回傳值即為procOrm之回傳值。
|
|
89
|
-
後處理只在check、前處理與ORM操作皆成功後執行,逐筆失敗(含del)或mix部分寫入後reject者皆不執行;後處理自身亦可reject。後處理之input
|
|
93
|
+
後處理只在check、前處理與ORM操作皆成功後執行,逐筆失敗(含del)或mix部分寫入後reject者皆不執行;後處理自身亦可reject。後處理之input為前處理後之數據(無前處理者即呼叫端傳入之數據),不含本套件之補值與主鍵轉型,主鍵見output。
|
|
90
94
|
如需觀察ORM實際寫入,可另監聽woItems內各ORM之change與error事件;惟事件由ORM發出,不含userId,mix會依步驟分次發出,經woItems直接寫入者亦會發出,insert之結果為聚合計數而非逐筆。
|
|
91
95
|
錯誤型別:procOrm由本套件產生之reject一律為英文訊息字串(供前端顯示或對應多語系;ORM之錯誤原文另由console.log輸出而不轉出),擴充函數自身之reject或拋錯則原樣轉出;建構(設定或ORM無效)、擴充函數註冊、backup與recover由本套件產生之錯誤一律為Error,ORM之錯誤則原樣轉出。</p>
|
|
92
96
|
<p>授權:procOrm不做授權,不檢查使用者,亦不依使用者過濾數據;userId只供寫入稽核欄位(userId、userIdUpdate)與交給擴充函數。procOrm開放給前端時(如讓前端經資料通道直接操作資料表),須由開放處負責授權;addFunCheck可掛額外檢查,惟伺服端程式之呼叫亦會經過。
|
|
@@ -96,7 +100,7 @@ insert、save與mix之新增、修改,寫入前各欄位之字串皆經wsemi
|
|
|
96
100
|
|
|
97
101
|
<dt class="tag-source">Source:</dt>
|
|
98
102
|
<dd class="tag-source"><ul class="dummy"><li>
|
|
99
|
-
<a href="WServOrm.mjs.html">WServOrm.mjs</a>, <a href="WServOrm.mjs.html#
|
|
103
|
+
<a href="WServOrm.mjs.html">WServOrm.mjs</a>, <a href="WServOrm.mjs.html#line100">line 100</a>
|
|
100
104
|
</li></ul></dd>
|
|
101
105
|
|
|
102
106
|
|
|
@@ -439,7 +443,7 @@ insert、save與mix之新增、修改,寫入前各欄位之字串皆經wsemi
|
|
|
439
443
|
<br class="clear">
|
|
440
444
|
|
|
441
445
|
<footer>
|
|
442
|
-
Documentation generated by <a href="https://github.com/jsdoc3/jsdoc">JSDoc 4.0.5</a> on
|
|
446
|
+
Documentation generated by <a href="https://github.com/jsdoc3/jsdoc">JSDoc 4.0.5</a> on Wed Oct 14 2026 07:49:10 GMT+0800 (台北標準時間) using the <a href="https://github.com/clenemt/docdash">docdash</a> theme.
|
|
443
447
|
</footer>
|
|
444
448
|
|
|
445
449
|
<script>prettyPrint();</script>
|
package/docs/WServOrm.mjs.html
CHANGED
|
@@ -45,34 +45,15 @@
|
|
|
45
45
|
|
|
46
46
|
<section>
|
|
47
47
|
<article>
|
|
48
|
-
<pre class="prettyprint source linenums"><code>import
|
|
49
|
-
import pmSeries from 'wsemi/src/pmSeries.mjs'
|
|
48
|
+
<pre class="prettyprint source linenums"><code>import pmSeries from 'wsemi/src/pmSeries.mjs'
|
|
50
49
|
import isfun from 'wsemi/src/isfun.mjs'
|
|
50
|
+
import checkOptRemoved from './checkOptRemoved.mjs'
|
|
51
51
|
import getDbBackupAndRecover from './getDbBackupAndRecover.mjs'
|
|
52
52
|
import getWoItems from './getWoItems.mjs'
|
|
53
53
|
import getMapOrm from './getMapOrm.mjs'
|
|
54
54
|
import getProc from './getProc.mjs'
|
|
55
55
|
|
|
56
56
|
|
|
57
|
-
function checkOptRemoved(opt) {
|
|
58
|
-
//已移除之設定: 未給、null或false者接受(使用端現行之設定), 其餘值拋錯, 避免使用端以為仍有檢查而無聲失去
|
|
59
|
-
//useCheckUser只查使用者存在且有效, 屬認證而非授權, 卻曾被當成授權; procOrm不做授權, 須由procOrm開放給前端之處負責
|
|
60
|
-
//useExcludeWhenNotAdmin依賴只有useCheckUser才產生之isAdmin, 未開useCheckUser時會把所有人當非管理者過濾, 一併移除
|
|
61
|
-
let msgAuth = 'procOrm does not authorize users, authorize where procOrm is exposed to clients'
|
|
62
|
-
let ks = [
|
|
63
|
-
['useCheckUser', msgAuth],
|
|
64
|
-
['getUserById', msgAuth],
|
|
65
|
-
['useExcludeWhenNotAdmin', 'procOrm does not filter select results by user, use addFunPostProcessing if needed'],
|
|
66
|
-
]
|
|
67
|
-
for (let [k, hint] of ks) {
|
|
68
|
-
let v = get(opt, k)
|
|
69
|
-
if (v !== undefined && v !== null && v !== false) {
|
|
70
|
-
throw new Error(`opt.${k} has been removed, ${hint}`)
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
|
|
76
57
|
/**
|
|
77
58
|
* 產生伺服器ORM的支援物件
|
|
78
59
|
*
|
|
@@ -80,17 +61,21 @@ function checkOptRemoved(opt) {
|
|
|
80
61
|
*
|
|
81
62
|
* procOrm之save、del與mix,於ORM回傳之逐筆結果有任一筆ok為0(單筆失敗)時reject,訊息列出失敗筆之主鍵,如'failed to save data: id[b]'(超過100字元者截斷顯示;無有效主鍵者以data[k]列其於輸入內之位置,k自0起;超過10筆者只列前10筆並附總筆數),錯誤原文另由console.log輸出。
|
|
82
63
|
* 單筆失敗不中斷整批,故reject時其餘筆數已寫入。
|
|
83
|
-
* mix之各步驟遇單筆失敗不中斷後續步驟,待全部步驟完成後一併reject;遇整批性錯誤則立即reject,後續步驟不執行。
|
|
64
|
+
* mix之各步驟遇單筆失敗不中斷後續步驟,待全部步驟完成後一併reject;遇整批性錯誤則立即reject,後續步驟不執行。mix於resolve時回傳各步驟所處理之列之id(依add、del、diff之序串接,del之列不分是否命中)。
|
|
84
65
|
*
|
|
85
|
-
* insert、save與mix之新增、修改,數據未帶有效主鍵(
|
|
86
|
-
*
|
|
66
|
+
* insert、save與mix之新增、修改,數據未帶有效主鍵(未給、null、空字串,或非字串非數字之值,含NaN與±Infinity)者,若資料表之主鍵欄為id且為字串型(或型別未知;型別如STRING(36)者以括號前之字判定),由本套件產生主鍵(同各ORM之genIDSeq)並於resolve時回傳;單一空物件與只含一個空物件之陣列不補(ORM視為無效輸入而不寫入),多筆批次中之空物件列則補。
|
|
67
|
+
* 主鍵欄為字串型(或型別未知)之資料表,主鍵為有限數字者一律以其字串形式處置(如123視為'123'):寫入、resolve回傳、del與mix之刪除,以及select、delAll條件中主鍵之比較($eq、$ne、$gt、$gte、$lt、$lte、$in、$nin、$all,含$not、$and、$or、$nor之內)皆同,故以數字或其字串形式寫入、查詢與刪除者為同一筆,範圍比較依字串之字典序;主鍵欄為整數型者不轉型。轉型只作用於procOrm,經woItems直接操作者不轉型。舊版w-orm-mongodb(2025-04-29以前)保留之數字主鍵資料,以數字查詢將查不到。
|
|
68
|
+
* reject時不回傳補出之主鍵,故重送未帶主鍵之數據會再產生新主鍵而重複寫入,需冪等重送者請自帶主鍵(非空字串或有限數字);save帶主鍵者重送以主鍵覆寫,del重送時已刪除者為主鍵未命中。
|
|
69
|
+
* 回傳之id為本套件依輸入(含補值與轉型)所得之主鍵,非ORM之寫入結果;ORM之比對規則較寬者(如w-orm-mdb之Jet不分大小寫、忽略尾端空白),insert略過或save命中之對象可能與回傳之id不同。lmdb、lowdb、level、mongodb等主鍵固定為字串id之ORM,其資料表之id須為字串型pk或不標型別;本套件以id為主鍵,主鍵欄非id之ORM(如w-orm-postgresql預設為time)之回傳id不具主鍵意義。
|
|
70
|
+
* 輸入之形狀:insert與save之各筆、mix之新增與修改之各筆須為物件,mix之add、del、diff若給予須為陣列,delAll之條件若給予須為物件,否則於任何寫入之前reject,如'failed to insert data, not an object: data[0]'、'invalid find of delAll, need an object'。
|
|
71
|
+
* mix之add與del、del與diff含同一主鍵(依上述轉型後)者,於任何步驟之前整批reject且不寫入任何一筆,訊息列其位置而不回顯主鍵,如'invalid input of mix, same id in add and del: add[0], del[1]';因mix依add、del、diff之序執行,同鍵將使新增因已存在而略過後即被刪除。
|
|
87
72
|
* insert(含mix之新增)為主鍵已存在即略過之冪等新增,回傳之id含被略過者;recover亦以insert還原,故資料表須先清空。
|
|
88
73
|
* insert、save與mix之新增、修改,寫入前各欄位之字串皆經wsemi之clearXSS清除(白名單外之標籤轉義,白名單內之標籤刪除白名單外之屬性與危險網址,標籤外之文字只轉義會開始標籤之<,故'a < b'等數學式不改寫);select與del不經清除。
|
|
89
74
|
* 字串主鍵若會被清除改寫(含HTML標記,如<後緊接英文字母),整批reject且不寫入任何一筆(mix於任何步驟之前檢查新增與修改),訊息列其位置而不回顯主鍵,如'failed to save data, id contains HTML markup: data[1]';否則改寫後之主鍵與呼叫端所持者不同,以原主鍵查詢與刪除皆找不到。
|
|
90
75
|
*
|
|
91
76
|
* 擴充函數(addFunCheck、addFunPreProcessing、addFunPostProcessing)屬於各實例,重建實例後須重新註冊;註冊時須傳入函數,否則立即拋錯(addFunCheck另令該實例之procOrm一律reject);註冊後約1毫秒(以計時器)才生效,在此之前之procOrm呼叫不會套用。
|
|
92
77
|
* 呼叫契約:check以reject或拋錯阻擋,回傳值不影響結果,且早於前處理執行;前處理逐一呼叫,各收新建之參數物件{woName,userId,mode,input},input為前一個回傳之input,回傳須為含input之物件否則reject;後處理逐一呼叫,各收新建之參數物件且其output為前一個之回傳值,最後之回傳值即為procOrm之回傳值。
|
|
93
|
-
* 後處理只在check、前處理與ORM操作皆成功後執行,逐筆失敗(含del)或mix部分寫入後reject者皆不執行;後處理自身亦可reject。後處理之input
|
|
78
|
+
* 後處理只在check、前處理與ORM操作皆成功後執行,逐筆失敗(含del)或mix部分寫入後reject者皆不執行;後處理自身亦可reject。後處理之input為前處理後之數據(無前處理者即呼叫端傳入之數據),不含本套件之補值與主鍵轉型,主鍵見output。
|
|
94
79
|
* 如需觀察ORM實際寫入,可另監聽woItems內各ORM之change與error事件;惟事件由ORM發出,不含userId,mix會依步驟分次發出,經woItems直接寫入者亦會發出,insert之結果為聚合計數而非逐筆。
|
|
95
80
|
* 錯誤型別:procOrm由本套件產生之reject一律為英文訊息字串(供前端顯示或對應多語系;ORM之錯誤原文另由console.log輸出而不轉出),擴充函數自身之reject或拋錯則原樣轉出;建構(設定或ORM無效)、擴充函數註冊、backup與recover由本套件產生之錯誤一律為Error,ORM之錯誤則原樣轉出。
|
|
96
81
|
*
|
|
@@ -161,7 +146,7 @@ function checkOptRemoved(opt) {
|
|
|
161
146
|
*/
|
|
162
147
|
function WServOrm(ds, WOrm, url, db, opt = {}) {
|
|
163
148
|
|
|
164
|
-
//checkOptRemoved, 須於建立各資料表ORM之前, 否則拋錯時已開啟之ORM(如lmdb之檔案)
|
|
149
|
+
//checkOptRemoved, 須於建立各資料表ORM之前, 否則拋錯時已開啟之ORM(如lmdb之檔案)無從關閉; getProc收到opt時會再檢查一次, 無副作用
|
|
165
150
|
checkOptRemoved(opt)
|
|
166
151
|
|
|
167
152
|
//getWoItems
|
|
@@ -173,7 +158,7 @@ function WServOrm(ds, WOrm, url, db, opt = {}) {
|
|
|
173
158
|
// console.log('mapOrm', mapOrm)
|
|
174
159
|
|
|
175
160
|
//getProc
|
|
176
|
-
let proc = getProc(mapOrm)
|
|
161
|
+
let proc = getProc(mapOrm, opt)
|
|
177
162
|
// console.log('proc', proc)
|
|
178
163
|
|
|
179
164
|
//closeAll, 逐一關閉woItems內各資料表ORM實例之連線(env), 釋放檔案鎖, 供測試隔離或優雅關機使用
|
|
@@ -210,7 +195,7 @@ export default WServOrm
|
|
|
210
195
|
<br class="clear">
|
|
211
196
|
|
|
212
197
|
<footer>
|
|
213
|
-
Documentation generated by <a href="https://github.com/jsdoc3/jsdoc">JSDoc 4.0.5</a> on
|
|
198
|
+
Documentation generated by <a href="https://github.com/jsdoc3/jsdoc">JSDoc 4.0.5</a> on Wed Oct 14 2026 07:49:10 GMT+0800 (台北標準時間) using the <a href="https://github.com/clenemt/docdash">docdash</a> theme.
|
|
214
199
|
</footer>
|
|
215
200
|
|
|
216
201
|
<script>prettyPrint();</script>
|
package/docs/index.html
CHANGED
|
@@ -71,7 +71,7 @@
|
|
|
71
71
|
<br class="clear">
|
|
72
72
|
|
|
73
73
|
<footer>
|
|
74
|
-
Documentation generated by <a href="https://github.com/jsdoc3/jsdoc">JSDoc 4.0.5</a> on
|
|
74
|
+
Documentation generated by <a href="https://github.com/jsdoc3/jsdoc">JSDoc 4.0.5</a> on Wed Oct 14 2026 07:49:10 GMT+0800 (台北標準時間) using the <a href="https://github.com/clenemt/docdash">docdash</a> theme.
|
|
75
75
|
</footer>
|
|
76
76
|
|
|
77
77
|
<script>prettyPrint();</script>
|
package/package.json
CHANGED
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "w-serv-orm",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.11",
|
|
4
4
|
"main": "dist/w-serv-orm.umd.js",
|
|
5
5
|
"dependencies": {
|
|
6
6
|
"json5": "^2.2.3",
|
|
7
7
|
"w-data-collector": "^1.0.46",
|
|
8
|
-
"wsemi": "^1.9.
|
|
8
|
+
"wsemi": "^1.9.15"
|
|
9
9
|
},
|
|
10
10
|
"devDependencies": {
|
|
11
11
|
"w-orm-lowdb": "^1.0.31",
|
|
12
|
-
"w-package-tools": "^1.1.
|
|
12
|
+
"w-package-tools": "^1.1.18"
|
|
13
13
|
},
|
|
14
14
|
"scripts": {
|
|
15
15
|
"test": "mocha --parallel \"test/*.test.mjs\" --timeout 180000",
|
package/src/WServOrm.mjs
CHANGED
|
@@ -1,31 +1,12 @@
|
|
|
1
|
-
import get from 'lodash-es/get.js'
|
|
2
1
|
import pmSeries from 'wsemi/src/pmSeries.mjs'
|
|
3
2
|
import isfun from 'wsemi/src/isfun.mjs'
|
|
3
|
+
import checkOptRemoved from './checkOptRemoved.mjs'
|
|
4
4
|
import getDbBackupAndRecover from './getDbBackupAndRecover.mjs'
|
|
5
5
|
import getWoItems from './getWoItems.mjs'
|
|
6
6
|
import getMapOrm from './getMapOrm.mjs'
|
|
7
7
|
import getProc from './getProc.mjs'
|
|
8
8
|
|
|
9
9
|
|
|
10
|
-
function checkOptRemoved(opt) {
|
|
11
|
-
//已移除之設定: 未給、null或false者接受(使用端現行之設定), 其餘值拋錯, 避免使用端以為仍有檢查而無聲失去
|
|
12
|
-
//useCheckUser只查使用者存在且有效, 屬認證而非授權, 卻曾被當成授權; procOrm不做授權, 須由procOrm開放給前端之處負責
|
|
13
|
-
//useExcludeWhenNotAdmin依賴只有useCheckUser才產生之isAdmin, 未開useCheckUser時會把所有人當非管理者過濾, 一併移除
|
|
14
|
-
let msgAuth = 'procOrm does not authorize users, authorize where procOrm is exposed to clients'
|
|
15
|
-
let ks = [
|
|
16
|
-
['useCheckUser', msgAuth],
|
|
17
|
-
['getUserById', msgAuth],
|
|
18
|
-
['useExcludeWhenNotAdmin', 'procOrm does not filter select results by user, use addFunPostProcessing if needed'],
|
|
19
|
-
]
|
|
20
|
-
for (let [k, hint] of ks) {
|
|
21
|
-
let v = get(opt, k)
|
|
22
|
-
if (v !== undefined && v !== null && v !== false) {
|
|
23
|
-
throw new Error(`opt.${k} has been removed, ${hint}`)
|
|
24
|
-
}
|
|
25
|
-
}
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
|
|
29
10
|
/**
|
|
30
11
|
* 產生伺服器ORM的支援物件
|
|
31
12
|
*
|
|
@@ -33,17 +14,21 @@ function checkOptRemoved(opt) {
|
|
|
33
14
|
*
|
|
34
15
|
* procOrm之save、del與mix,於ORM回傳之逐筆結果有任一筆ok為0(單筆失敗)時reject,訊息列出失敗筆之主鍵,如'failed to save data: id[b]'(超過100字元者截斷顯示;無有效主鍵者以data[k]列其於輸入內之位置,k自0起;超過10筆者只列前10筆並附總筆數),錯誤原文另由console.log輸出。
|
|
35
16
|
* 單筆失敗不中斷整批,故reject時其餘筆數已寫入。
|
|
36
|
-
* mix之各步驟遇單筆失敗不中斷後續步驟,待全部步驟完成後一併reject;遇整批性錯誤則立即reject,後續步驟不執行。
|
|
17
|
+
* mix之各步驟遇單筆失敗不中斷後續步驟,待全部步驟完成後一併reject;遇整批性錯誤則立即reject,後續步驟不執行。mix於resolve時回傳各步驟所處理之列之id(依add、del、diff之序串接,del之列不分是否命中)。
|
|
37
18
|
*
|
|
38
|
-
* insert、save與mix之新增、修改,數據未帶有效主鍵(
|
|
39
|
-
*
|
|
19
|
+
* insert、save與mix之新增、修改,數據未帶有效主鍵(未給、null、空字串,或非字串非數字之值,含NaN與±Infinity)者,若資料表之主鍵欄為id且為字串型(或型別未知;型別如STRING(36)者以括號前之字判定),由本套件產生主鍵(同各ORM之genIDSeq)並於resolve時回傳;單一空物件與只含一個空物件之陣列不補(ORM視為無效輸入而不寫入),多筆批次中之空物件列則補。
|
|
20
|
+
* 主鍵欄為字串型(或型別未知)之資料表,主鍵為有限數字者一律以其字串形式處置(如123視為'123'):寫入、resolve回傳、del與mix之刪除,以及select、delAll條件中主鍵之比較($eq、$ne、$gt、$gte、$lt、$lte、$in、$nin、$all,含$not、$and、$or、$nor之內)皆同,故以數字或其字串形式寫入、查詢與刪除者為同一筆,範圍比較依字串之字典序;主鍵欄為整數型者不轉型。轉型只作用於procOrm,經woItems直接操作者不轉型。舊版w-orm-mongodb(2025-04-29以前)保留之數字主鍵資料,以數字查詢將查不到。
|
|
21
|
+
* reject時不回傳補出之主鍵,故重送未帶主鍵之數據會再產生新主鍵而重複寫入,需冪等重送者請自帶主鍵(非空字串或有限數字);save帶主鍵者重送以主鍵覆寫,del重送時已刪除者為主鍵未命中。
|
|
22
|
+
* 回傳之id為本套件依輸入(含補值與轉型)所得之主鍵,非ORM之寫入結果;ORM之比對規則較寬者(如w-orm-mdb之Jet不分大小寫、忽略尾端空白),insert略過或save命中之對象可能與回傳之id不同。lmdb、lowdb、level、mongodb等主鍵固定為字串id之ORM,其資料表之id須為字串型pk或不標型別;本套件以id為主鍵,主鍵欄非id之ORM(如w-orm-postgresql預設為time)之回傳id不具主鍵意義。
|
|
23
|
+
* 輸入之形狀:insert與save之各筆、mix之新增與修改之各筆須為物件,mix之add、del、diff若給予須為陣列,delAll之條件若給予須為物件,否則於任何寫入之前reject,如'failed to insert data, not an object: data[0]'、'invalid find of delAll, need an object'。
|
|
24
|
+
* mix之add與del、del與diff含同一主鍵(依上述轉型後)者,於任何步驟之前整批reject且不寫入任何一筆,訊息列其位置而不回顯主鍵,如'invalid input of mix, same id in add and del: add[0], del[1]';因mix依add、del、diff之序執行,同鍵將使新增因已存在而略過後即被刪除。
|
|
40
25
|
* insert(含mix之新增)為主鍵已存在即略過之冪等新增,回傳之id含被略過者;recover亦以insert還原,故資料表須先清空。
|
|
41
26
|
* insert、save與mix之新增、修改,寫入前各欄位之字串皆經wsemi之clearXSS清除(白名單外之標籤轉義,白名單內之標籤刪除白名單外之屬性與危險網址,標籤外之文字只轉義會開始標籤之<,故'a < b'等數學式不改寫);select與del不經清除。
|
|
42
27
|
* 字串主鍵若會被清除改寫(含HTML標記,如<後緊接英文字母),整批reject且不寫入任何一筆(mix於任何步驟之前檢查新增與修改),訊息列其位置而不回顯主鍵,如'failed to save data, id contains HTML markup: data[1]';否則改寫後之主鍵與呼叫端所持者不同,以原主鍵查詢與刪除皆找不到。
|
|
43
28
|
*
|
|
44
29
|
* 擴充函數(addFunCheck、addFunPreProcessing、addFunPostProcessing)屬於各實例,重建實例後須重新註冊;註冊時須傳入函數,否則立即拋錯(addFunCheck另令該實例之procOrm一律reject);註冊後約1毫秒(以計時器)才生效,在此之前之procOrm呼叫不會套用。
|
|
45
30
|
* 呼叫契約:check以reject或拋錯阻擋,回傳值不影響結果,且早於前處理執行;前處理逐一呼叫,各收新建之參數物件{woName,userId,mode,input},input為前一個回傳之input,回傳須為含input之物件否則reject;後處理逐一呼叫,各收新建之參數物件且其output為前一個之回傳值,最後之回傳值即為procOrm之回傳值。
|
|
46
|
-
* 後處理只在check、前處理與ORM操作皆成功後執行,逐筆失敗(含del)或mix部分寫入後reject者皆不執行;後處理自身亦可reject。後處理之input
|
|
31
|
+
* 後處理只在check、前處理與ORM操作皆成功後執行,逐筆失敗(含del)或mix部分寫入後reject者皆不執行;後處理自身亦可reject。後處理之input為前處理後之數據(無前處理者即呼叫端傳入之數據),不含本套件之補值與主鍵轉型,主鍵見output。
|
|
47
32
|
* 如需觀察ORM實際寫入,可另監聽woItems內各ORM之change與error事件;惟事件由ORM發出,不含userId,mix會依步驟分次發出,經woItems直接寫入者亦會發出,insert之結果為聚合計數而非逐筆。
|
|
48
33
|
* 錯誤型別:procOrm由本套件產生之reject一律為英文訊息字串(供前端顯示或對應多語系;ORM之錯誤原文另由console.log輸出而不轉出),擴充函數自身之reject或拋錯則原樣轉出;建構(設定或ORM無效)、擴充函數註冊、backup與recover由本套件產生之錯誤一律為Error,ORM之錯誤則原樣轉出。
|
|
49
34
|
*
|
|
@@ -114,7 +99,7 @@ function checkOptRemoved(opt) {
|
|
|
114
99
|
*/
|
|
115
100
|
function WServOrm(ds, WOrm, url, db, opt = {}) {
|
|
116
101
|
|
|
117
|
-
//checkOptRemoved, 須於建立各資料表ORM之前, 否則拋錯時已開啟之ORM(如lmdb之檔案)
|
|
102
|
+
//checkOptRemoved, 須於建立各資料表ORM之前, 否則拋錯時已開啟之ORM(如lmdb之檔案)無從關閉; getProc收到opt時會再檢查一次, 無副作用
|
|
118
103
|
checkOptRemoved(opt)
|
|
119
104
|
|
|
120
105
|
//getWoItems
|
|
@@ -126,7 +111,7 @@ function WServOrm(ds, WOrm, url, db, opt = {}) {
|
|
|
126
111
|
// console.log('mapOrm', mapOrm)
|
|
127
112
|
|
|
128
113
|
//getProc
|
|
129
|
-
let proc = getProc(mapOrm)
|
|
114
|
+
let proc = getProc(mapOrm, opt)
|
|
130
115
|
// console.log('proc', proc)
|
|
131
116
|
|
|
132
117
|
//closeAll, 逐一關閉woItems內各資料表ORM實例之連線(env), 釋放檔案鎖, 供測試隔離或優雅關機使用
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import get from 'lodash-es/get.js'
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
function checkOptRemoved(opt) {
|
|
5
|
+
//已移除之設定: 未給、null或false者接受(使用端現行之設定), 其餘值拋錯, 避免使用端以為仍有檢查而無聲失去
|
|
6
|
+
//useCheckUser只查使用者存在且有效, 屬認證而非授權, 卻曾被當成授權; procOrm不做授權, 須由procOrm開放給前端之處負責
|
|
7
|
+
//useExcludeWhenNotAdmin依賴只有useCheckUser才產生之isAdmin, 未開useCheckUser時會把所有人當非管理者過濾, 一併移除
|
|
8
|
+
//收opt之進入點(WServOrm、getProc)皆須呼叫, 直接呼叫getProc者亦不得靜默忽略
|
|
9
|
+
//使用端包裝層可直接呼叫以檢查其自身之opt, 只看這三鍵, opt之其他鍵不受影響, 通過時回傳undefined
|
|
10
|
+
let msgAuth = 'procOrm does not authorize users, authorize where procOrm is exposed to clients'
|
|
11
|
+
let ks = [
|
|
12
|
+
['useCheckUser', msgAuth],
|
|
13
|
+
['getUserById', msgAuth],
|
|
14
|
+
['useExcludeWhenNotAdmin', 'procOrm does not filter select results by user, use addFunPostProcessing if needed'],
|
|
15
|
+
]
|
|
16
|
+
for (let [k, hint] of ks) {
|
|
17
|
+
let v = get(opt, k)
|
|
18
|
+
if (v !== undefined && v !== null && v !== false) {
|
|
19
|
+
throw new Error(`opt.${k} has been removed, ${hint}`)
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
export default checkOptRemoved
|
package/src/getMapOrm.mjs
CHANGED
|
@@ -8,6 +8,8 @@ import truncate from 'lodash-es/truncate.js'
|
|
|
8
8
|
import nowms2str from 'wsemi/src/nowms2str.mjs'
|
|
9
9
|
import genIDSeq from 'wsemi/src/genIDSeq.mjs'
|
|
10
10
|
import isarr from 'wsemi/src/isarr.mjs'
|
|
11
|
+
import isearr from 'wsemi/src/isearr.mjs'
|
|
12
|
+
import isobj from 'wsemi/src/isobj.mjs'
|
|
11
13
|
import iseobj from 'wsemi/src/iseobj.mjs'
|
|
12
14
|
import isestr from 'wsemi/src/isestr.mjs'
|
|
13
15
|
import isnum from 'wsemi/src/isnum.mjs'
|
|
@@ -18,6 +20,7 @@ import getItemsFail from 'w-data-collector/src/getItemsFail.mjs'
|
|
|
18
20
|
function getMsgFail(msg, fails) {
|
|
19
21
|
//前端訊息僅列出失敗筆, 錯誤原文另由console.log輸出, 與既有整批錯誤之處理一致
|
|
20
22
|
//有效主鍵(非空字串或數字)列為id[...], 否則列其於輸入內之位置, 失敗筆數過多時僅列前nShow筆並附總筆數
|
|
23
|
+
//呼叫端給予位置標籤ref者(如mix同鍵之add[0])原樣列出
|
|
21
24
|
//主鍵超過nIdMax字元者截斷顯示(LMDB逐筆失敗常因主鍵超長, 故兩者常同時出現), log仍保留全文
|
|
22
25
|
|
|
23
26
|
//nShow, nIdMax
|
|
@@ -26,6 +29,9 @@ function getMsgFail(msg, fails) {
|
|
|
26
29
|
|
|
27
30
|
//ss
|
|
28
31
|
let ss = map(take(fails, nShow), (v) => {
|
|
32
|
+
if (isestr(v.ref)) {
|
|
33
|
+
return v.ref
|
|
34
|
+
}
|
|
29
35
|
if (isestr(v.id) || isnum(v.id)) {
|
|
30
36
|
return `id[${truncate(String(v.id), { length: nIdMax, omission: '…' })}]`
|
|
31
37
|
}
|
|
@@ -55,8 +61,9 @@ function isPkIdString(dsTab, keys) {
|
|
|
55
61
|
}
|
|
56
62
|
|
|
57
63
|
//check, 型別已知者須為字串型, 與w-orm-reladb可自動補值之主鍵欄型別一致
|
|
64
|
+
//比對前取「(」之前之字並轉大寫, 如STRING(36)視為STRING, 同w-orm-reladb以type.key判定而不含長度參數
|
|
58
65
|
let type = get(settings, 'id.type', '') || get(dsTab, 'kpType.id', '')
|
|
59
|
-
if (isestr(type) && ['STRING', 'TEXT', 'CHAR', 'UUID', 'CITEXT'].indexOf(type.toUpperCase()) < 0) {
|
|
66
|
+
if (isestr(type) && ['STRING', 'TEXT', 'CHAR', 'UUID', 'CITEXT'].indexOf(type.split('(')[0].trim().toUpperCase()) < 0) {
|
|
60
67
|
return false
|
|
61
68
|
}
|
|
62
69
|
|
|
@@ -64,6 +71,147 @@ function isPkIdString(dsTab, keys) {
|
|
|
64
71
|
}
|
|
65
72
|
|
|
66
73
|
|
|
74
|
+
function isIdCast(ds, woName) {
|
|
75
|
+
//資料表之主鍵欄為字串型或型別未知者, 主鍵之有限數字以其字串形式處置(R-ID-02); ds無該表者不轉型, 令select與del照舊原樣交ORM
|
|
76
|
+
let dsTab = get(ds, woName, null)
|
|
77
|
+
let keys = get(dsTab, 'keys', null)
|
|
78
|
+
if (!isarr(keys)) {
|
|
79
|
+
return false
|
|
80
|
+
}
|
|
81
|
+
return isPkIdString(dsTab, keys)
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
function isIdNumFinite(v) {
|
|
86
|
+
//主鍵值是否為有限數字, 不用wsemi之isnum: 其對數字字串與Infinity亦為true
|
|
87
|
+
return typeof v === 'number' && Number.isFinite(v)
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
function castIdValue(v) {
|
|
92
|
+
//主鍵值之正規化: 有限數字轉為其字串形式, 其餘原樣(NaN、±Infinity亦原樣, 寫入時由adjustData視為未提供而補值)
|
|
93
|
+
//各後端之主鍵判準互異, lmdb、lowdb、level、mongodb只認非空字串而以genIDSeq取代數字, reladb、mdb之字串欄由資料庫把數字存為字串,
|
|
94
|
+
//故由本套件先轉, 令寫入、刪除與查詢條件同一規則, 回傳之主鍵與實際寫入者一致
|
|
95
|
+
return isIdNumFinite(v) ? String(v) : v
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
function castIdRows(rows) {
|
|
100
|
+
//寫入與刪除之數據: 各列之主鍵正規化, 寫於複本, 不改呼叫端之物件; 非物件之列原樣
|
|
101
|
+
let one = !isarr(rows)
|
|
102
|
+
let rs = map(one ? [rows] : rows, (v) => {
|
|
103
|
+
if (isobj(v) && isIdNumFinite(v.id)) {
|
|
104
|
+
return { ...v, id: castIdValue(v.id) }
|
|
105
|
+
}
|
|
106
|
+
return v
|
|
107
|
+
})
|
|
108
|
+
return one ? rs[0] : rs
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
function castIdCond(c) {
|
|
113
|
+
//主鍵之條件: 值或運算子物件; 比較運算子之值(陣列逐一)轉型, $not遞迴, 其餘運算子($regex、$exists等)原樣
|
|
114
|
+
if (!isobj(c)) {
|
|
115
|
+
return castIdValue(c)
|
|
116
|
+
}
|
|
117
|
+
let r = {}
|
|
118
|
+
each(c, (v, k) => {
|
|
119
|
+
if (k === '$not') {
|
|
120
|
+
r[k] = castIdCond(v)
|
|
121
|
+
}
|
|
122
|
+
else if (['$eq', '$ne', '$gt', '$gte', '$lt', '$lte', '$in', '$nin', '$all'].indexOf(k) >= 0) {
|
|
123
|
+
r[k] = isarr(v) ? map(v, castIdValue) : castIdValue(v)
|
|
124
|
+
}
|
|
125
|
+
else {
|
|
126
|
+
r[k] = v
|
|
127
|
+
}
|
|
128
|
+
})
|
|
129
|
+
return r
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
function castIdFind(find) {
|
|
134
|
+
//查詢條件(select、delAll之find): id之條件轉型, 遞迴$and、$or、$nor; 他欄原樣; 寫於複本, 非物件原樣
|
|
135
|
+
if (!isobj(find)) {
|
|
136
|
+
return find
|
|
137
|
+
}
|
|
138
|
+
let r = {}
|
|
139
|
+
each(find, (v, k) => {
|
|
140
|
+
if (k === 'id') {
|
|
141
|
+
r[k] = castIdCond(v)
|
|
142
|
+
}
|
|
143
|
+
else if (['$and', '$or', '$nor'].indexOf(k) >= 0 && isarr(v)) {
|
|
144
|
+
r[k] = map(v, castIdFind)
|
|
145
|
+
}
|
|
146
|
+
else {
|
|
147
|
+
r[k] = v
|
|
148
|
+
}
|
|
149
|
+
})
|
|
150
|
+
return r
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
|
|
154
|
+
function getItemsNotObj(data) {
|
|
155
|
+
//寫入之各筆須為一般物件(R-INPUT-01), 回傳[{k}], k為其於輸入內之位置; 單一非物件輸入視為第0筆
|
|
156
|
+
//非物件之元素經adjustData之clearXSS逐鍵複製時, 字串、陣列被展開成{0:...,1:...}、數字等變成{}, 寫入垃圾紀錄
|
|
157
|
+
let rs = []
|
|
158
|
+
each(isarr(data) ? data : [data], (v, k) => {
|
|
159
|
+
if (!isobj(v)) {
|
|
160
|
+
rs.push({ k })
|
|
161
|
+
}
|
|
162
|
+
})
|
|
163
|
+
return rs
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
|
|
167
|
+
function getMixIdKey(v) {
|
|
168
|
+
//mix跨段比對用之主鍵鍵值(R-MIX-01): 以castIdValue正規化(有限數字取其字串形式, 與寫入、刪除之轉型同一函數), 正規化後為非空字串者取之, 其餘(未提供)不參與比對
|
|
169
|
+
//不論資料表是否適用轉型(R-ID-02)皆正規化, 故整數主鍵欄之5與'5'亦視為同鍵
|
|
170
|
+
let id = castIdValue(get(v, 'id'))
|
|
171
|
+
return isestr(id) ? id : null
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
|
|
175
|
+
function getMsgsMixSameId(input) {
|
|
176
|
+
//mix之add與del、del與diff含同一主鍵者回傳各組之訊息(R-MIX-01), 位置以段名[k]列出而不回顯主鍵
|
|
177
|
+
//mix依add、del、diff之序執行, add與del同鍵將使新增因已存在而略過後即被刪除, del與diff同鍵將使刪除後由save以部份欄位重新插入
|
|
178
|
+
let msgs = []
|
|
179
|
+
for (let [ka, kb] of [['add', 'del'], ['del', 'diff']]) {
|
|
180
|
+
|
|
181
|
+
//ma, 前段各主鍵之位置
|
|
182
|
+
let ma = new Map()
|
|
183
|
+
each(get(input, ka, []), (v, k) => {
|
|
184
|
+
let key = getMixIdKey(v)
|
|
185
|
+
if (key !== null) {
|
|
186
|
+
ma.set(key, [...(ma.get(key) || []), k])
|
|
187
|
+
}
|
|
188
|
+
})
|
|
189
|
+
|
|
190
|
+
//ia, ib, 同鍵者之位置
|
|
191
|
+
let ia = []
|
|
192
|
+
let ib = []
|
|
193
|
+
each(get(input, kb, []), (v, k) => {
|
|
194
|
+
let key = getMixIdKey(v)
|
|
195
|
+
if (key !== null && ma.has(key)) {
|
|
196
|
+
ia = [...ia, ...ma.get(key)]
|
|
197
|
+
ib.push(k)
|
|
198
|
+
}
|
|
199
|
+
})
|
|
200
|
+
|
|
201
|
+
//msgs
|
|
202
|
+
if (size(ib) > 0) {
|
|
203
|
+
let refs = [
|
|
204
|
+
...map([...new Set(ia)].sort((a, b) => a - b), (k) => ({ ref: `${ka}[${k}]` })),
|
|
205
|
+
...map(ib, (k) => ({ ref: `${kb}[${k}]` })),
|
|
206
|
+
]
|
|
207
|
+
msgs.push(getMsgFail(`invalid input of mix, same id in ${ka} and ${kb}`, refs))
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
}
|
|
211
|
+
return msgs
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
|
|
67
215
|
function getItemsIdXSS(data) {
|
|
68
216
|
//輸入數據之主鍵(非空字串)會被clearXSS改寫者, 回傳[{k,id}], k為其於輸入內之位置
|
|
69
217
|
//寫入路徑之欄位皆經clearXSS而select與del不經過, 主鍵被改寫後以原主鍵查詢與刪除皆找不到, 故呼叫端須於寫入前整批拒絕
|
|
@@ -172,15 +320,20 @@ function adjustData(ds, woName, userId, data, mode = 'save') { //mode: 'insert',
|
|
|
172
320
|
})
|
|
173
321
|
}
|
|
174
322
|
|
|
175
|
-
//id, 未提供有效主鍵者由本套件補值, 令回傳之主鍵與實際寫入者一致, 補值器同各ORM之genIDSeq
|
|
176
|
-
|
|
177
|
-
//補值寫在上方之複本,
|
|
323
|
+
//id, 未提供有效主鍵者由本套件補值, 令回傳之主鍵與實際寫入者一致, 補值器同各ORM之genIDSeq(R-ID-01)
|
|
324
|
+
//有效主鍵為非空字串: 字串型主鍵欄之有限數字已於mapOrm轉為字串(R-ID-02), 剩餘之數字(NaN、±Infinity)與其他型別視為未提供
|
|
325
|
+
//補值寫在上方之複本, 不改呼叫端原物件
|
|
326
|
+
//輸入依wsemi之iseobj、isearr判為無效者(單一空物件、只含一個空物件之陣列)不補, 保留ORM視之為無效輸入(規格T5)而不寫入之行為;
|
|
327
|
+
//多筆批次中之空物件列則補, 因後端照常補值寫入, 不補則回傳undefined而與實際不符
|
|
178
328
|
if (isPkIdString(ds[woName], keys)) {
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
329
|
+
let effective = one ? iseobj(data[0]) : isearr(data)
|
|
330
|
+
if (effective) {
|
|
331
|
+
each(data, (v) => {
|
|
332
|
+
if (!isestr(v.id)) {
|
|
333
|
+
v.id = genIDSeq()
|
|
334
|
+
}
|
|
335
|
+
})
|
|
336
|
+
}
|
|
184
337
|
}
|
|
185
338
|
|
|
186
339
|
//復原data型態, 若原本data為物件則取第0個回傳
|
|
@@ -208,10 +361,13 @@ async function mapOrm(ds, woItems, userId, woName, mode, input) {
|
|
|
208
361
|
return Promise.reject(`can not find table[${woName}] in ORM`)
|
|
209
362
|
}
|
|
210
363
|
|
|
364
|
+
//idCast, 主鍵之有限數字以其字串形式處置(R-ID-02), 寫入、刪除與查詢條件共用同一判定
|
|
365
|
+
let idCast = isIdCast(ds, woName)
|
|
366
|
+
|
|
211
367
|
if (mode === 'select') {
|
|
212
368
|
|
|
213
|
-
//select
|
|
214
|
-
r = await wo.select(input)
|
|
369
|
+
//select, 條件中主鍵之比較值轉型(R-ID-02)
|
|
370
|
+
r = await wo.select(idCast ? castIdFind(input) : input)
|
|
215
371
|
.catch((msg) => {
|
|
216
372
|
console.log(`${woName}.select catch`, msg)
|
|
217
373
|
err = 'failed to select data'
|
|
@@ -228,11 +384,26 @@ async function mapOrm(ds, woItems, userId, woName, mode, input) {
|
|
|
228
384
|
//inputOrig, 呼叫端原輸入, 逐筆失敗之訊息以呼叫端視角列出(本套件補出之主鍵只寫於複本, reject時呼叫端未曾取得)
|
|
229
385
|
let inputOrig = input
|
|
230
386
|
|
|
387
|
+
//msgMode
|
|
388
|
+
let msgMode = mode === 'insert' ? 'failed to insert data' : 'failed to save data'
|
|
389
|
+
|
|
390
|
+
//check, 各筆須為一般物件, 否則整批拒絕且不寫入任何一筆(R-INPUT-01)
|
|
391
|
+
let failsNotObj = getItemsNotObj(input)
|
|
392
|
+
if (size(failsNotObj) > 0) {
|
|
393
|
+
console.log(`${woName}.${mode} data is not an object`, failsNotObj)
|
|
394
|
+
return Promise.reject(getMsgFail(`${msgMode}, not an object`, failsNotObj))
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
//castIdRows, 主鍵之有限數字轉為字串, 寫於複本(R-ID-02)
|
|
398
|
+
if (idCast) {
|
|
399
|
+
input = castIdRows(input)
|
|
400
|
+
}
|
|
401
|
+
|
|
231
402
|
//check, 主鍵會被clearXSS改寫者整批拒絕, 不寫入任何一筆
|
|
232
403
|
let failsIdXSS = getItemsIdXSS(input)
|
|
233
404
|
if (size(failsIdXSS) > 0) {
|
|
234
405
|
console.log(`${woName}.${mode} id changed by clearXSS`, failsIdXSS)
|
|
235
|
-
return Promise.reject(getMsgIdXSS(
|
|
406
|
+
return Promise.reject(getMsgIdXSS(msgMode, failsIdXSS))
|
|
236
407
|
}
|
|
237
408
|
|
|
238
409
|
//adjustData
|
|
@@ -290,6 +461,11 @@ async function mapOrm(ds, woItems, userId, woName, mode, input) {
|
|
|
290
461
|
}
|
|
291
462
|
else if (mode === 'del') {
|
|
292
463
|
|
|
464
|
+
//castIdRows, 主鍵之有限數字轉為字串, 寫於複本(R-ID-02); 刪除不經clearXSS(R-XSS-01)
|
|
465
|
+
if (idCast) {
|
|
466
|
+
input = castIdRows(input)
|
|
467
|
+
}
|
|
468
|
+
|
|
293
469
|
//del
|
|
294
470
|
r = await wo.del(input)
|
|
295
471
|
.catch((msg) => {
|
|
@@ -309,8 +485,14 @@ async function mapOrm(ds, woItems, userId, woName, mode, input) {
|
|
|
309
485
|
}
|
|
310
486
|
else if (mode === 'delAll') {
|
|
311
487
|
|
|
312
|
-
//
|
|
313
|
-
|
|
488
|
+
//check, 條件若給予須為一般物件(R-INPUT-01): 各後端對非物件之條件一律視為無條件而刪除全表; 未給(undefined)與空物件依規格為刪除全部
|
|
489
|
+
if (input !== undefined && !isobj(input)) {
|
|
490
|
+
console.log(`${woName}.delAll invalid find`, input)
|
|
491
|
+
return Promise.reject('invalid find of delAll, need an object')
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
//delAll, 條件中主鍵之比較值轉型(R-ID-02)
|
|
495
|
+
r = await wo.delAll(idCast ? castIdFind(input) : input)
|
|
314
496
|
.catch((msg) => {
|
|
315
497
|
console.log(`${woName}.delAll catch`, msg)
|
|
316
498
|
err = 'failed to delete data'
|
|
@@ -324,6 +506,36 @@ async function mapOrm(ds, woItems, userId, woName, mode, input) {
|
|
|
324
506
|
return Promise.reject('invalid input of mix, need the result object of ltdtDiffByKey')
|
|
325
507
|
}
|
|
326
508
|
|
|
509
|
+
//check, add、del、diff若給予須為陣列(R-INPUT-01), 否則各段之回傳將逐欄位產生
|
|
510
|
+
for (let key of ['add', 'del', 'diff']) {
|
|
511
|
+
if (input[key] !== undefined && !isarr(input[key])) {
|
|
512
|
+
return Promise.reject('invalid input of mix, need the result object of ltdtDiffByKey')
|
|
513
|
+
}
|
|
514
|
+
}
|
|
515
|
+
|
|
516
|
+
//check, 新增與修改之各筆須為一般物件, 於任何步驟寫入之前整批拒絕(R-INPUT-01)
|
|
517
|
+
let msgsNotObj = []
|
|
518
|
+
for (let [key, msg] of [['add', 'failed to insert data'], ['diff', 'failed to update data']]) {
|
|
519
|
+
let fails = getItemsNotObj(get(input, key, []))
|
|
520
|
+
if (size(fails) > 0) {
|
|
521
|
+
console.log(`${woName}.mix ${key} data is not an object`, fails)
|
|
522
|
+
msgsNotObj.push(getMsgFail(`${msg}, not an object`, fails))
|
|
523
|
+
}
|
|
524
|
+
}
|
|
525
|
+
if (size(msgsNotObj) > 0) {
|
|
526
|
+
return Promise.reject(join(msgsNotObj, '; '))
|
|
527
|
+
}
|
|
528
|
+
|
|
529
|
+
//castIdRows, 各段主鍵之有限數字轉為字串, 寫於複本(R-ID-02)
|
|
530
|
+
if (idCast) {
|
|
531
|
+
input = { ...input }
|
|
532
|
+
for (let key of ['add', 'del', 'diff']) {
|
|
533
|
+
if (isarr(input[key])) {
|
|
534
|
+
input[key] = castIdRows(input[key])
|
|
535
|
+
}
|
|
536
|
+
}
|
|
537
|
+
}
|
|
538
|
+
|
|
327
539
|
//check, 新增與修改之主鍵會被clearXSS改寫者, 於任何步驟寫入之前整批拒絕
|
|
328
540
|
let msgsIdXSS = []
|
|
329
541
|
for (let [key, msg] of [['add', 'failed to insert data'], ['diff', 'failed to update data']]) {
|
|
@@ -337,6 +549,13 @@ async function mapOrm(ds, woItems, userId, woName, mode, input) {
|
|
|
337
549
|
return Promise.reject(join(msgsIdXSS, '; '))
|
|
338
550
|
}
|
|
339
551
|
|
|
552
|
+
//check, add與del、del與diff同鍵者, 於任何步驟寫入之前整批拒絕(R-MIX-01)
|
|
553
|
+
let msgsSameId = getMsgsMixSameId(input)
|
|
554
|
+
if (size(msgsSameId) > 0) {
|
|
555
|
+
console.log(`${woName}.mix same id across steps`, msgsSameId)
|
|
556
|
+
return Promise.reject(join(msgsSameId, '; '))
|
|
557
|
+
}
|
|
558
|
+
|
|
340
559
|
//rt,r
|
|
341
560
|
let rt
|
|
342
561
|
r = [] //default to array
|