Compare commits
649 Commits
Author | SHA1 | Date |
---|---|---|
|
58585c1526 | |
|
7abd2ecddc | |
|
819e1845c7 | |
|
394d16a96f | |
|
29f45692e9 | |
|
01d8584a7f | |
|
7fce7dd559 | |
|
331cca66dd | |
|
a25e2da920 | |
|
84618aead6 | |
|
3f910f9d2b | |
|
2f4ff8b6a9 | |
|
7bc1d917dd | |
|
99cd30a7a4 | |
|
de40642e89 | |
|
8233e2abed | |
|
685d1da15c | |
|
163499308f | |
|
c92e5862d0 | |
|
dd40c27e8a | |
|
c2c892b071 | |
|
260acdd898 | |
|
9ae71251ae | |
|
d35149e2b7 | |
|
034cb0048b | |
|
ca091d0342 | |
|
a84721b8d8 | |
|
602602f69a | |
|
26d4b30634 | |
|
b7a7963223 | |
|
12ebf9c5e6 | |
|
23b7ca8cff | |
|
feaecba71c | |
|
a206198e31 | |
|
13f2ce6b98 | |
|
68bff40ba7 | |
|
79a06a587c | |
|
b83f9e68e9 | |
|
a7efa3576b | |
|
edbdf8520b | |
|
1f5a4cd072 | |
|
365f27b911 | |
|
99ff788169 | |
|
8de60b558e | |
|
5bd2b4a863 | |
|
5bdf70874f | |
|
ba8b93e692 | |
|
b6b240afc9 | |
|
c4fac50863 | |
|
d7083ec8f2 | |
|
911f29e8df | |
|
5673cbd52b | |
|
714dc55ccd | |
|
838905877f | |
|
138cd90d6c | |
|
52cd087aff | |
|
ed7cabb668 | |
|
dee64a9b4c | |
|
a35e8d18c2 | |
|
33d06b5c71 | |
|
748fe22bcb | |
|
901486f0d3 | |
|
60959e36c3 | |
|
b603283dc0 | |
|
9d47253316 | |
|
03c1683a83 | |
|
07d3e1bbf7 | |
|
e9af46e836 | |
|
3a50b1356e | |
|
726476f8e0 | |
|
5c9e59845f | |
|
3be58c9446 | |
|
df89ae59e3 | |
|
f191c8a5dd | |
|
3268b036d5 | |
|
9b96f69098 | |
|
14abfbd6dc | |
|
8e642d7686 | |
|
be3faa1ee2 | |
|
fbd4b63b24 | |
|
0c9aca3b36 | |
|
da0863edf0 | |
|
70520a7812 | |
|
b4c23eb6d1 | |
|
ad8b95b2eb | |
|
e24936078c | |
|
4f8af05cdf | |
|
7a847a04d1 | |
|
da54829f83 | |
|
2eae64f3cc | |
|
4557e2e10f | |
|
469d4802bb | |
|
76fe5ad1c7 | |
|
f80a351225 | |
|
82247a34ae | |
|
9fa099484f | |
|
15a9e785a7 | |
|
01a942cae0 | |
|
3ab268dc3c | |
|
1dd3546c3f | |
|
2137f28948 | |
|
244ec17e60 | |
|
ea84ff4314 | |
|
f937e63d25 | |
|
b6f88b6a2a | |
|
603ebced82 | |
|
75482a9acc | |
|
d6f6910980 | |
|
8a3880289d | |
|
ae8597883a | |
|
d584420397 | |
|
ae9695fe3c | |
|
92c26b4100 | |
|
ef397c07e9 | |
|
7454c4a150 | |
|
dac2d47312 | |
|
23e1a4a4c2 | |
|
cdec276338 | |
|
a2bae7dabf | |
|
b7e9f4d00a | |
|
5f1161a8ab | |
|
584a5ccb82 | |
|
28d4007fdc | |
|
2667d3b7d9 | |
|
7926b52480 | |
|
da06e6ea60 | |
|
30751df17d | |
|
bbd63ce67e | |
|
9846fceaa6 | |
|
ef210ef56a | |
|
f2b02920ba | |
|
42430be43b | |
|
8562200853 | |
|
3469bded5a | |
|
8b6cad7d66 | |
|
40bb3c0b79 | |
|
757f48a539 | |
|
1ea6ceb0d1 | |
|
ac34f8141e | |
|
ea3cdb1a71 | |
|
90564e3100 | |
|
90e1485d8c | |
|
19e9d633bc | |
|
9d5899cab2 | |
|
1a4a6ae780 | |
|
7cbc8a86ee | |
|
8306749436 | |
|
518e3ca1ca | |
|
415dfda681 | |
|
8f43d9b659 | |
|
354235247b | |
|
861cc5c448 | |
|
8716469dc2 | |
|
fa18610f96 | |
|
f7ded61ad5 | |
|
bab4c90553 | |
|
3bd71ff26f | |
|
0d58339bb8 | |
|
23e76f905e | |
|
363bdcea5d | |
|
6959b9c620 | |
|
3b121482cf | |
|
76cdc59461 | |
|
1c197d8b17 | |
|
2f7ea50341 | |
|
6729c8a0ec | |
|
cdfc71ec77 | |
|
15da93ef83 | |
|
7184fb054a | |
|
b6cd522d44 | |
|
07333dfe8b | |
|
f14ae8af4b | |
|
025b22a9af | |
|
4908768c02 | |
|
3dea22d63e | |
|
ae3cf25e77 | |
|
c905f025df | |
|
1977e5470b | |
|
ab90d50eff | |
|
fb725e9ff0 | |
|
8a126f623d | |
|
8b29d8db01 | |
|
d395dbb24d | |
|
52c6532add | |
|
0b9cee7f3b | |
|
efc5d1d6f8 | |
|
28debb4c75 | |
|
a927649bc8 | |
|
521bfba219 | |
|
dfa614e025 | |
|
ff623d6a0d | |
|
f6b2508dfd | |
|
2f6dd870a6 | |
|
46aaa540bd | |
|
9e928208d0 | |
|
9cf7686a36 | |
|
d72ced71e0 | |
|
04b84b5e1b | |
|
9b66ad190e | |
|
4fb97fcd04 | |
|
66abcebe8a | |
|
abf82d7eca | |
|
58c59cad72 | |
|
2b4ad689ad | |
|
a229a7ac58 | |
|
888e66bf6a | |
|
2a233c7274 | |
|
b7e8f5ff66 | |
|
59b61d5627 | |
|
07958c4076 | |
|
80e2b2112d | |
|
d274a48192 | |
|
d2b09ebf94 | |
|
c61a47e88e | |
|
66af10f401 | |
|
69f010e4ca | |
|
8e8fe00564 | |
|
15f1a85fdc | |
|
c5eeb88a93 | |
|
253ecfcbfb | |
|
9df18e2a75 | |
|
8dce904fc6 | |
|
d31b9d8b97 | |
|
2f993718f0 | |
|
e20550b680 | |
|
316ebce660 | |
|
c03b0a0440 | |
|
e486faf93a | |
|
a883c06bbc | |
|
9e34360aae | |
|
e8a401c347 | |
|
a97aed958c | |
|
936be6bbff | |
|
236df1d649 | |
|
dde1101793 | |
|
86ef5a48a9 | |
|
69cb28ade1 | |
|
3b59084cd0 | |
|
340db473ed | |
|
492d569903 | |
|
99d56bba79 | |
|
8b8fc962c7 | |
|
df78022886 | |
|
abf7bbaa1e | |
|
b50cbb3cfd | |
|
713a2775f2 | |
|
21870e41ce | |
|
b4441976eb | |
|
37ae2919e2 | |
|
bc2f0fd705 | |
|
8cd336407c | |
|
e34b63ea79 | |
|
cbf3fe162d | |
|
00b9903fac | |
|
0e519f7559 | |
|
0f759fe0aa | |
|
6ede40fee4 | |
|
6ffad9f7d6 | |
|
d29bec72a8 | |
|
cb20ae6575 | |
|
31ea05783a | |
|
2789a92899 | |
|
1d0f914867 | |
|
e6da14dcd1 | |
|
7b4b0d3140 | |
|
ac49c3ecd2 | |
|
3e6a6367cc | |
|
91e255d850 | |
|
1d2d6cb4e1 | |
|
b97a28db7b | |
|
3a1dca1e87 | |
|
70e859fa66 | |
|
ed951a6a61 | |
|
e9b42dcd74 | |
|
f45c92c1f8 | |
|
82f11f0235 | |
|
49cb048a45 | |
|
837240b7f9 | |
|
12a502b097 | |
|
30d27f00c4 | |
|
00a0693db0 | |
|
a0659aefa0 | |
|
5f1888b41a | |
|
4bc39fdec1 | |
|
caea97e3f5 | |
|
15b1335250 | |
|
9b8123a14c | |
|
ed1c5079f6 | |
|
53942e621a | |
|
a1321b9ecb | |
|
b84c5201b1 | |
|
f8d7ca9474 | |
|
53767a6995 | |
|
bc9c4defd1 | |
|
615b95dde9 | |
|
5dbc0cab83 | |
|
775cbbf2b9 | |
|
a4eb2f0377 | |
|
c021fc9abf | |
|
d9eac14926 | |
|
2db05f56e1 | |
|
ba09182cea | |
|
be09d5774e | |
|
21ea203a58 | |
|
2fcbb1bd77 | |
|
b4c5e22403 | |
|
3f39054504 | |
|
9a6d0d83ca | |
|
f30fd1819c | |
|
6aef060d52 | |
|
b3b5dc2cd5 | |
|
5665ff69d5 | |
|
bb577e61b5 | |
|
6dfa9b859c | |
|
bc50aa7c4d | |
|
b810bd88ca | |
|
3ebf47c4ea | |
|
5f6a5d5201 | |
|
e647559995 | |
|
de008c40e3 | |
|
c748ad1d26 | |
|
a935fabcd1 | |
|
2f95d901bd | |
|
6186b57659 | |
|
d7554634fc | |
|
e3addf9f93 | |
|
e30c2c8dbd | |
|
8d4de43c8a | |
|
3b92a09e41 | |
|
26bd2225f1 | |
|
f60d4b0cca | |
|
2cd612c4c3 | |
|
07cc90e40f | |
|
d04a6c13d1 | |
|
2be3b8be62 | |
|
92e05e6537 | |
|
2691470504 | |
|
74d429d52d | |
|
fe82f6ac79 | |
|
816b948ab0 | |
|
e3448fad03 | |
|
9a58695740 | |
|
5aa2d71a23 | |
|
cc5557bd11 | |
|
a0de6bc760 | |
|
06428247ad | |
|
05898ed9f6 | |
|
454fd0f1b2 | |
|
1e9ac69f2c | |
|
d2a594fc08 | |
|
8487ee1bc0 | |
|
8c95930cd6 | |
|
8031e54fb4 | |
|
76acf333fd | |
|
261dd1c865 | |
|
c80aa9abe8 | |
|
35e032d1df | |
|
a0f1d4a979 | |
|
dd23a14b24 | |
|
7835a43cce | |
|
c884c62b3b | |
|
c24e498191 | |
|
6e6a3df9f0 | |
|
f13d50ea3f | |
|
0ba09a1c4f | |
|
b9929b83fe | |
|
b1cd7eff5c | |
|
8d1690e9b2 | |
|
259c0b9cc6 | |
|
4c53abad32 | |
|
429d13e537 | |
|
a25df6ac36 | |
|
7292d206e5 | |
|
4c665cd6f7 | |
|
74c6776575 | |
|
c83f82d5a0 | |
|
e026b4c4af | |
|
095ddd654e | |
|
9bcf42488d | |
|
280228960a | |
|
04336a60cf | |
|
b1766518a8 | |
|
f1f7bf2031 | |
|
6d65afc35f | |
|
c6e8779a06 | |
|
df1c8dc00e | |
|
9fe64d412c | |
|
8919115439 | |
|
df394a613b | |
|
670a328bc3 | |
|
bf931b50c2 | |
|
d94327a8f4 | |
|
376315b7a4 | |
|
2946c5b489 | |
|
39daef79d8 | |
|
cdbe715acc | |
|
a50b3db6e9 | |
|
33884062f4 | |
|
b01cd74b19 | |
|
c7b88e9b8e | |
|
65a358656f | |
|
92654f646b | |
|
dcfda58fda | |
|
36303b3b63 | |
|
f4ec02b316 | |
|
baeb76e911 | |
|
3a08b9d9ae | |
|
251ced811b | |
|
d2b4d567d9 | |
|
ad3e1e8d8a | |
|
b6f9e92ef8 | |
|
d19e189fd7 | |
|
a456700ea2 | |
|
af4f4dfa87 | |
|
abdd5675b0 | |
|
002137dd5d | |
|
b328934b6c | |
|
c555dedfeb | |
|
19048cd716 | |
|
2f867576b6 | |
|
606880bf79 | |
|
a9611a0430 | |
|
1d4ad278c9 | |
|
1b7858a857 | |
|
770f11b3a6 | |
|
89503bb233 | |
|
89a964e919 | |
|
b30fbf8c1e | |
|
d54d769477 | |
|
f921a4f630 | |
|
5578ab4ea7 | |
|
a1817a40e6 | |
|
b9f0284a28 | |
|
b07bdfc71a | |
|
28d457d357 | |
|
8b75767daf | |
|
ab11205fa0 | |
|
23949703b0 | |
|
063e0f173f | |
|
071f93762c | |
|
15251880a1 | |
|
8946516614 | |
|
1e8b8a041b | |
|
ad1777fcd3 | |
|
6f491dc756 | |
|
fa7c38cad8 | |
|
464610ef0a | |
|
814c55c7cd | |
|
ac955826f5 | |
|
a90dc0eac4 | |
|
bca631518b | |
|
ad4edc6a15 | |
|
74c43ca358 | |
|
f1fa976f50 | |
|
dd7167b533 | |
|
ca4ede014e | |
|
6b83b6f4d1 | |
|
e3c276720f | |
|
6478c73dde | |
|
78c5b12740 | |
|
6aea50ad27 | |
|
0c2bb81dac | |
|
40286fcd28 | |
|
c3103a2077 | |
|
1ed385e393 | |
|
39555a010a | |
|
85a29caaa5 | |
|
5539ef7ac6 | |
|
313925a5e7 | |
|
612cf05fdf | |
|
19e7a47489 | |
|
58b3c4f148 | |
|
d49774c590 | |
|
5fcad43aab | |
|
df4d89279a | |
|
acb667b73d | |
|
5b44c08f7a | |
|
04523260c2 | |
|
a5ba7408ff | |
|
413f31631e | |
|
aa20180c4f | |
|
344441d48d | |
|
caf9d09274 | |
|
853a41641b | |
|
5369cf3d54 | |
|
91ab3e9162 | |
|
ef0257e338 | |
|
de4718d5b8 | |
|
98eb783a80 | |
|
2aa4d20394 | |
|
0b871d1de1 | |
|
9c94b9344b | |
|
3fba79e725 | |
|
78c4bfbc2d | |
|
b0e3de1083 | |
|
08e8725ba5 | |
|
837c74d08e | |
|
6677c58cff | |
|
eff6458a2a | |
|
0bba44ee29 | |
|
76673a8ed8 | |
|
511c4763d3 | |
|
88312422d8 | |
|
6793be20ca | |
|
5c3405eceb | |
|
47addd943a | |
|
48af738ec4 | |
|
1534392c62 | |
|
fe83f3cfd1 | |
|
b3945fcd33 | |
|
11f9c1f23a | |
|
b4d89fc7c5 | |
|
7288277e87 | |
|
095fe62fa6 | |
|
a8d0d78913 | |
|
7d69bdab70 | |
|
55b10a43c4 | |
|
84eee5a69e | |
|
e45292de08 | |
|
ff5a3fa8bf | |
|
3f9448f5d2 | |
|
80d8fcd8b1 | |
|
5a7c0326ab | |
|
ce7194ff6a | |
|
75a8c6a309 | |
|
01df3705d1 | |
|
a391c83a2c | |
|
70b3bb6aae | |
|
5e0c73bec7 | |
|
c7d23d18d0 | |
|
4b705b127c | |
|
4d288813e2 | |
|
c0a778eeab | |
|
fd99c6dc6e | |
|
4c69781504 | |
|
928747f795 | |
|
81fc722b72 | |
|
0ec951ee11 | |
|
0b81425a13 | |
|
d3f63a8ecd | |
|
92499aa152 | |
|
caa36a21da | |
|
47ef38e9f1 | |
|
09e2208062 | |
|
bd5fd07d27 | |
|
9e0f624ad5 | |
|
422ec9ad4f | |
|
261ca1f092 | |
|
8684df62a6 | |
|
84322d07a9 | |
|
e513c0d355 | |
|
e834368502 | |
|
0cf0fa95ff | |
|
fda332d60b | |
|
3c2669ed7d | |
|
5f713f0319 | |
|
b2cbf99e73 | |
|
f9131aa18f | |
|
49330322ef | |
|
8f095520f4 | |
|
916d80c36d | |
|
f6e1df87be | |
|
cc276643a2 | |
|
a39cbebf94 | |
|
0f0b391f67 | |
|
65548b6fe0 | |
|
f0a6bd146b | |
|
35f61c4aa4 | |
|
eb85b3e3a6 | |
|
3348d501e8 | |
|
7609585dc3 | |
|
dbe25f282d | |
|
8fa7c94605 | |
|
8ebb734a31 | |
|
726c6978fc | |
|
2af2599a7d | |
|
708826cc49 | |
|
104b861747 | |
|
a391762771 | |
|
0ce3f4ead9 | |
|
39ff54d392 | |
|
a761e0d114 | |
|
f2e718639a | |
|
f1f535846e | |
|
60e16298f2 | |
|
f7036b2837 | |
|
abaf29f74d | |
|
c95a969af7 | |
|
6e90ec6dcc | |
|
22764a095a | |
|
b285737382 | |
|
b6a43dd65a | |
|
5eb81aa47f | |
|
64eddf5d26 | |
|
1ddac5bddb | |
|
248d57da4e | |
|
eb921ed9d5 | |
|
fcac276955 | |
|
242fd1bb56 | |
|
7c7bd56b2e | |
|
7eec7e4e34 | |
|
251798c711 | |
|
2b8b6719a7 | |
|
1e59918c11 | |
|
4d14c1473f | |
|
06284be673 | |
|
76b87ade93 | |
|
9717e512b4 | |
|
535b000195 | |
|
3e5c805418 | |
|
7d50f6130b | |
|
be7d97c885 | |
|
bc2689f58d | |
|
38d5b5b4fa | |
|
7ae64edf3d | |
|
bc16692d9b | |
|
2f263898c6 | |
|
9bd7f8d02f | |
|
da728b8d70 | |
|
3ac18e4e06 | |
|
b180f363ac | |
|
6f3675b13c | |
|
548379ca2a | |
|
3938c3755e | |
|
9af79cf51a | |
|
fa0039c6a8 | |
|
6846ef87b4 | |
|
5d762102fe | |
|
751fd7b245 | |
|
7a8213af43 | |
|
5d8c36eca0 | |
|
d2ee73b9d3 | |
|
e2d75e70d1 | |
|
26c795fdea | |
|
c479be7069 | |
|
77f11cda3b | |
|
ef3191d3e5 | |
|
2b4b44292a | |
|
dafb8a1ab7 | |
|
41c9a6d9b0 | |
|
5a66f9ad72 | |
|
dd30054138 | |
|
d31c43bc09 | |
|
eddb17689c | |
|
2bb77e60e5 | |
|
965655be3a | |
|
e49eb47e36 | |
|
63f8b3b323 | |
|
90163ba709 |
|
@ -1 +1,2 @@
|
|||
coverage
|
||||
dist
|
||||
|
|
|
@ -1,37 +0,0 @@
|
|||
<!--
|
||||
Questions:
|
||||
https://groups.google.com/forum/#!forum/loopbackjs
|
||||
https://gitter.im/strongloop/loopback
|
||||
Immediate support:
|
||||
https://strongloop.com/api-connect-faqs/
|
||||
https://strongloop.com/node-js/subscription-plans/
|
||||
-->
|
||||
|
||||
# Description/Steps to reproduce
|
||||
|
||||
<!--
|
||||
If feature: A description of the feature
|
||||
If bug: Steps to reproduce
|
||||
-->
|
||||
|
||||
# Link to reproduction sandbox
|
||||
|
||||
<!--
|
||||
Link to an app sandbox for reproduction
|
||||
|
||||
Note: Failure to provide a sandbox application for reproduction purposes will result in the issue being closed.
|
||||
-->
|
||||
|
||||
# Expected result
|
||||
|
||||
<!--
|
||||
Also include actual results if bug
|
||||
-->
|
||||
|
||||
# Additional information
|
||||
|
||||
<!--
|
||||
Copy+paste the output of these two commands:
|
||||
node -e 'console.log(process.platform, process.arch, process.versions.node)'
|
||||
npm ls --prod --depth 0 | grep loopback
|
||||
-->
|
|
@ -0,0 +1,53 @@
|
|||
---
|
||||
name: Bug report
|
||||
about: Create a report to help us improve
|
||||
labels: bug
|
||||
|
||||
---
|
||||
|
||||
<!-- 🚨 STOP 🚨 STOP 🚨 STOP 🚨
|
||||
|
||||
Are you using LoopBack version 4? Please report the bug here:
|
||||
https://github.com/strongloop/loopback-next/issues/new
|
||||
|
||||
HELP US HELP YOU, PLEASE
|
||||
- Do a quick search to avoid duplicate issues
|
||||
- Provide as much information as possible (reproduction sandbox, use case for features, etc.)
|
||||
- Consider using a more suitable venue for questions such as Stack Overflow, Gitter, etc.
|
||||
|
||||
Please fill in the *entire* template below.
|
||||
|
||||
-->
|
||||
|
||||
## Steps to reproduce
|
||||
|
||||
<!-- Describe how to reproduce the issue -->
|
||||
|
||||
## Current Behavior
|
||||
|
||||
<!-- Describe the observed result -->
|
||||
|
||||
## Expected Behavior
|
||||
|
||||
<!-- Describe what did you expect instead, what is the desired outcome? -->
|
||||
|
||||
## Link to reproduction sandbox
|
||||
|
||||
<!--
|
||||
See https://loopback.io/doc/en/contrib/Reporting-issues.html#loopback-3x-bugs
|
||||
Note: Failure to provide a sandbox application for reproduction purposes will result in the issue being closed.
|
||||
-->
|
||||
|
||||
## Additional information
|
||||
|
||||
<!--
|
||||
Copy+paste the output of these two commands:
|
||||
node -e 'console.log(process.platform, process.arch, process.versions.node)'
|
||||
npm ls --prod --depth 0 | grep loopback
|
||||
-->
|
||||
|
||||
## Related Issues
|
||||
|
||||
<!-- Did you find other bugs that looked similar? -->
|
||||
|
||||
_See [Reporting Issues](http://loopback.io/doc/en/contrib/Reporting-issues.html) for more tips on writing good issues_
|
|
@ -0,0 +1,34 @@
|
|||
---
|
||||
name: Feature request
|
||||
about: Suggest an idea for this project
|
||||
labels: feature
|
||||
|
||||
---
|
||||
|
||||
<!-- 🚨 STOP 🚨 STOP 🚨 STOP 🚨
|
||||
|
||||
LoopBack version 3 is in LTS mode, we are not accepting new features.
|
||||
|
||||
We are actively developing version 4, you can find the new GitHub
|
||||
repository here: https://github.com/strongloop/loopback-next
|
||||
|
||||
-->
|
||||
|
||||
## Suggestion
|
||||
|
||||
<!-- A summary of what you'd like to see added or changed -->
|
||||
|
||||
## Use Cases
|
||||
|
||||
<!--
|
||||
What do you want to use this for?
|
||||
What shortcomings exist with current approaches?
|
||||
-->
|
||||
|
||||
## Examples
|
||||
|
||||
<!-- Show how this would be used and what the behavior would be -->
|
||||
|
||||
## Acceptance criteria
|
||||
|
||||
TBD - will be filled by the team.
|
|
@ -0,0 +1,27 @@
|
|||
---
|
||||
name: Question
|
||||
about: The issue tracker is not for questions. Please use Stack Overflow or other resources for help.
|
||||
labels: question
|
||||
|
||||
---
|
||||
|
||||
<!-- 🚨 STOP 🚨 STOP 🚨 STOP 🚨
|
||||
|
||||
THE ISSUE TRACKER IS NOT FOR QUESTIONS.
|
||||
|
||||
DO NOT CREATE A NEW ISSUE TO ASK A QUESTION.
|
||||
|
||||
Please use one of the following resources for help:
|
||||
|
||||
**Questions**
|
||||
|
||||
- https://stackoverflow.com/tags/loopbackjs
|
||||
- https://groups.google.com/forum/#!forum/loopbackjs
|
||||
- https://gitter.im/strongloop/loopback
|
||||
|
||||
**Immediate support**
|
||||
|
||||
- https://strongloop.com/api-connect-faqs/
|
||||
- https://strongloop.com/node-js/subscription-plans/
|
||||
|
||||
-->
|
|
@ -0,0 +1,11 @@
|
|||
blank_issues_enabled: false
|
||||
contact_links:
|
||||
- name: Report a security vulnerability
|
||||
url: https://loopback.io/doc/en/contrib/Reporting-issues.html#security-issues
|
||||
about: Do not report security vulnerabilities using GitHub issues. Please send an email to `security@loopback.io` instead.
|
||||
- name: Get help on StackOverflow
|
||||
url: https://stackoverflow.com/tags/loopbackjs
|
||||
about: Please ask and answer questions on StackOverflow.
|
||||
- name: Join our mailing list
|
||||
url: https://groups.google.com/forum/#!forum/loopbackjs
|
||||
about: You can also post your question to our mailing list.
|
|
@ -1,25 +1,17 @@
|
|||
### Description
|
||||
|
||||
|
||||
#### Related issues
|
||||
|
||||
<!--
|
||||
Please use the following link syntaxes:
|
||||
Please provide a high-level description of the changes made by your pull request.
|
||||
|
||||
- connect to #49 (to reference issues in the current repository)
|
||||
- connect to strongloop/loopback#49 (to reference issues in another repository)
|
||||
Include references to all related GitHub issues and other pull requests, for example:
|
||||
|
||||
Fixes #123
|
||||
Implements #254
|
||||
See also #23
|
||||
-->
|
||||
|
||||
- connect to <link_to_referenced_issue>
|
||||
|
||||
### Checklist
|
||||
|
||||
<!--
|
||||
- Please mark your choice with an "x" (i.e. [x], see
|
||||
https://github.com/blog/1375-task-lists-in-gfm-issues-pulls-comments)
|
||||
- PR's without test coverage will be closed.
|
||||
-->
|
||||
## Checklist
|
||||
|
||||
- [ ] [Sign off your commits](https://loopback.io/doc/en/contrib/code-contrib.html) with DCO (Developer Certificate of Origin)
|
||||
- [ ] `npm test` passes on your machine
|
||||
- [ ] New tests added or existing tests modified to cover all changes
|
||||
- [ ] Code conforms with the [style
|
||||
guide](http://loopback.io/doc/en/contrib/style-guide.html)
|
||||
- [ ] Code conforms with the [style guide](https://loopback.io/doc/en/contrib/style-guide-es6.html)
|
||||
- [ ] Commit messages are following our [guidelines](https://loopback.io/doc/en/contrib/git-commit-messages.html)
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
paths-ignore: [test]
|
|
@ -9,6 +9,7 @@ exemptLabels:
|
|||
- critical
|
||||
- p1
|
||||
- major
|
||||
- "good first issue"
|
||||
# Label to use when marking an issue as stale
|
||||
staleLabel: stale
|
||||
# Comment to post when marking an issue as stale. Set to `false` to disable
|
||||
|
|
|
@ -0,0 +1,127 @@
|
|||
name: CI
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [master]
|
||||
pull_request:
|
||||
# The branches below must be a subset of the branches above
|
||||
branches: [master]
|
||||
schedule:
|
||||
- cron: '0 2 * * 1' # At 02:00 on Monday
|
||||
|
||||
permissions: {}
|
||||
|
||||
jobs:
|
||||
test:
|
||||
name: Test
|
||||
timeout-minutes: 15
|
||||
strategy:
|
||||
matrix:
|
||||
os: [ubuntu-latest]
|
||||
node-version: [18, 20]
|
||||
include:
|
||||
- os: macos-latest
|
||||
node-version: 18
|
||||
- os: windows-latest
|
||||
node-version: 18
|
||||
fail-fast: false
|
||||
runs-on: ${{ matrix.os }}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- name: Use Node.js ${{ matrix.node-version }}
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: ${{ matrix.node-version }}
|
||||
- name: Update NPM (Node.js v10)
|
||||
if: matrix.node-version == 10
|
||||
run: npm install --global npm@7
|
||||
- name: Update NPM
|
||||
if: matrix.node-version != 10
|
||||
run: npm install --global npm@8
|
||||
- name: Bootstrap project
|
||||
run: npm ci --ignore-scripts
|
||||
- name: Build project
|
||||
run: npm run build
|
||||
- name: Run tests
|
||||
run: npm test --ignore-scripts
|
||||
- name: Generate coverage report
|
||||
run: npx --no-install nyc report --reporter=lcov
|
||||
- name: Publish coverage report to Coveralls
|
||||
uses: coverallsapp/github-action@master
|
||||
with:
|
||||
flag-name: run-${{ matrix.os }}-node@${{ matrix.node-version }}
|
||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
parallel: true
|
||||
|
||||
posttest:
|
||||
name: Post-Test
|
||||
needs: test
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Coveralls finished
|
||||
uses: coverallsapp/github-action@master
|
||||
with:
|
||||
github-token: ${{ secrets.github_token }}
|
||||
parallel-finished: true
|
||||
|
||||
code-lint:
|
||||
name: Code Lint
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- name: Use Node.js 18
|
||||
uses: actions/setup-node@60edb5dd545a775178f52524783378180af0d1f8 # v4.0.2
|
||||
with:
|
||||
node-version: 18
|
||||
- name: Update NPM
|
||||
run: npm install --global npm
|
||||
- name: Bootstrap project
|
||||
run: npm ci --ignore-scripts
|
||||
- name: Build project
|
||||
run: npm run build
|
||||
- name: Verify code linting
|
||||
run: npm run lint
|
||||
|
||||
commit-lint:
|
||||
name: Commit Lint
|
||||
runs-on: ubuntu-latest
|
||||
if: ${{ github.event.pull_request }}
|
||||
steps:
|
||||
- uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- name: Use Node.js 18
|
||||
uses: actions/setup-node@60edb5dd545a775178f52524783378180af0d1f8 # v4.0.2
|
||||
with:
|
||||
node-version: 18
|
||||
- name: Update NPM
|
||||
run: npm install --global npm
|
||||
- name: Bootstrap project
|
||||
run: npm ci --ignore-scripts
|
||||
- name: Verify commit linting
|
||||
run: npx --no-install commitlint --from origin/master --to HEAD --verbose
|
||||
|
||||
codeql:
|
||||
name: CodeQL
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
permissions:
|
||||
# See: https://github.com/github/codeql-action/blob/008b2cc71c4cf3401f45919d8eede44a65b4a322/README.md#usage
|
||||
security-events: write
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@v3
|
||||
with:
|
||||
languages: 'javascript'
|
||||
config-file: ./.github/codeql/codeql-config.yaml
|
||||
|
||||
- name: Perform CodeQL Analysis
|
||||
uses: github/codeql-action/analyze@v3
|
|
@ -5,6 +5,8 @@ coverage
|
|||
v8.log
|
||||
.idea
|
||||
.DS_Store
|
||||
.git
|
||||
.vscode
|
||||
benchmark.js
|
||||
analyse.r
|
||||
docs/html
|
||||
|
@ -14,4 +16,3 @@ npm-debug.log
|
|||
test/memory.json
|
||||
.nyc_output
|
||||
dist
|
||||
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
reporter: dot
|
|
@ -3,7 +3,7 @@ doc
|
|||
coverage.html
|
||||
coverage
|
||||
v8.log
|
||||
|
||||
.git
|
||||
.DS_Store
|
||||
benchmark.js
|
||||
analyse.r
|
||||
|
@ -12,4 +12,4 @@ npm-debug.log
|
|||
.travis.yml
|
||||
.nyc_output
|
||||
dist
|
||||
|
||||
types/__test__.ts
|
||||
|
|
3
.nycrc
3
.nycrc
|
@ -2,5 +2,8 @@
|
|||
"exclude": [
|
||||
"test/**/*.js"
|
||||
],
|
||||
"reporter": [
|
||||
"html"
|
||||
],
|
||||
"cache": true
|
||||
}
|
||||
|
|
|
@ -0,0 +1,2 @@
|
|||
*.json
|
||||
*.md
|
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"bracketSpacing": false,
|
||||
"singleQuote": true,
|
||||
"printWidth": 80,
|
||||
"trailingComma": "all"
|
||||
}
|
30
.travis.yml
30
.travis.yml
|
@ -1,8 +1,26 @@
|
|||
sudo: false
|
||||
branches:
|
||||
only:
|
||||
- master
|
||||
arch:
|
||||
- arm64
|
||||
- ppc64le
|
||||
- s390x
|
||||
dist: bionic
|
||||
language: node_js
|
||||
before_install: |
|
||||
NODEJS_VERSION=$(node --version)
|
||||
if [ 'v10' = ${NODEJS_VERSION%%.*} ]
|
||||
then
|
||||
npm install --global npm@7
|
||||
else
|
||||
npm install --global npm@8
|
||||
fi
|
||||
script:
|
||||
- npm run --ignore-scripts build
|
||||
- npm test --ignore-scripts
|
||||
node_js:
|
||||
- "4"
|
||||
- "6"
|
||||
- "8"
|
||||
|
||||
after_success: npm run coverage
|
||||
- 10.24.1
|
||||
- 12.22.12
|
||||
- 14.21.3
|
||||
- 16.20.2
|
||||
- 17.9.1
|
||||
|
|
1091
CHANGES.md
1091
CHANGES.md
File diff suppressed because it is too large
Load Diff
|
@ -1,10 +1,10 @@
|
|||
# Lines starting with '#' are comments.
|
||||
# Each line is a file pattern followed by one or more owners,
|
||||
# the last matching pattern has the most precendence.
|
||||
# the last matching pattern has the most precedence.
|
||||
|
||||
# Current maintainers
|
||||
* @jannyHou @b-admike @virkt25 @dhmlau @shimks @zbarbuto @nitro404
|
||||
* @jannyHou @dhmlau @zbarbuto @nitro404 @emonddr
|
||||
|
||||
# Alumni
|
||||
#
|
||||
# @lehni @kjdelisle @loay @ssh24
|
||||
# @lehni @kjdelisle @loay @ssh24 @virkt25 @shimks @b-admike
|
||||
|
|
|
@ -0,0 +1,178 @@
|
|||
# Code of Conduct
|
||||
|
||||
LoopBack, as member project of the OpenJS Foundation, use
|
||||
[Contributor Covenant v2.0](https://contributor-covenant.org/version/2/0/code_of_conduct)
|
||||
as their code of conduct. The full text is included
|
||||
[below](#contributor-covenant-code-of-conduct-v2.0) in English, and translations
|
||||
are available from the Contributor Covenant organisation:
|
||||
|
||||
- [contributor-covenant.org/translations](https://www.contributor-covenant.org/translations)
|
||||
- [github.com/ContributorCovenant](https://github.com/ContributorCovenant/contributor_covenant/tree/release/content/version/2/0)
|
||||
|
||||
Refer to the sections on reporting and escalation in this document for the
|
||||
specific emails that can be used to report and escalate issues.
|
||||
|
||||
## Reporting
|
||||
|
||||
### Project Spaces
|
||||
|
||||
For reporting issues in spaces related to LoopBack, please use the email
|
||||
`tsc@loopback.io`. The LoopBack Technical Steering Committee (TSC) handles CoC issues related to the spaces that it
|
||||
maintains. The project TSC commits to:
|
||||
|
||||
- maintain the confidentiality with regard to the reporter of an incident
|
||||
- to participate in the path for escalation as outlined in the section on
|
||||
Escalation when required.
|
||||
|
||||
### Foundation Spaces
|
||||
|
||||
For reporting issues in spaces managed by the OpenJS Foundation, for example,
|
||||
repositories within the OpenJS organization, use the email
|
||||
`report@lists.openjsf.org`. The Cross Project Council (CPC) is responsible for
|
||||
managing these reports and commits to:
|
||||
|
||||
- maintain the confidentiality with regard to the reporter of an incident
|
||||
- to participate in the path for escalation as outlined in the section on
|
||||
Escalation when required.
|
||||
|
||||
## Escalation
|
||||
|
||||
The OpenJS Foundation maintains a Code of Conduct Panel (CoCP). This is a
|
||||
foundation-wide team established to manage escalation when a reporter believes
|
||||
that a report to a member project or the CPC has not been properly handled. In
|
||||
order to escalate to the CoCP send an email to
|
||||
`coc-escalation@lists.openjsf.org`.
|
||||
|
||||
For more information, refer to the full
|
||||
[Code of Conduct governance document](https://github.com/openjs-foundation/cross-project-council/blob/HEAD/CODE_OF_CONDUCT.md).
|
||||
|
||||
---
|
||||
|
||||
## Contributor Covenant Code of Conduct v2.0
|
||||
|
||||
## Our Pledge
|
||||
|
||||
We as members, contributors, and leaders pledge to make participation in our
|
||||
community a harassment-free experience for everyone, regardless of age, body
|
||||
size, visible or invisible disability, ethnicity, sex characteristics, gender
|
||||
identity and expression, level of experience, education, socio-economic status,
|
||||
nationality, personal appearance, race, religion, or sexual identity and
|
||||
orientation.
|
||||
|
||||
We pledge to act and interact in ways that contribute to an open, welcoming,
|
||||
diverse, inclusive, and healthy community.
|
||||
|
||||
## Our Standards
|
||||
|
||||
Examples of behavior that contributes to a positive environment for our
|
||||
community include:
|
||||
|
||||
- Demonstrating empathy and kindness toward other people
|
||||
- Being respectful of differing opinions, viewpoints, and experiences
|
||||
- Giving and gracefully accepting constructive feedback
|
||||
- Accepting responsibility and apologizing to those affected by our mistakes,
|
||||
and learning from the experience
|
||||
- Focusing on what is best not just for us as individuals, but for the overall
|
||||
community
|
||||
|
||||
Examples of unacceptable behavior include:
|
||||
|
||||
- The use of sexualized language or imagery, and sexual attention or advances of
|
||||
any kind
|
||||
- Trolling, insulting or derogatory comments, and personal or political attacks
|
||||
- Public or private harassment
|
||||
- Publishing others' private information, such as a physical or email address,
|
||||
without their explicit permission
|
||||
- Other conduct which could reasonably be considered inappropriate in a
|
||||
professional setting
|
||||
|
||||
## Enforcement Responsibilities
|
||||
|
||||
Community leaders are responsible for clarifying and enforcing our standards of
|
||||
acceptable behavior and will take appropriate and fair corrective action in
|
||||
response to any behavior that they deem inappropriate, threatening, offensive,
|
||||
or harmful.
|
||||
|
||||
Community leaders have the right and responsibility to remove, edit, or reject
|
||||
comments, commits, code, wiki edits, issues, and other contributions that are
|
||||
not aligned to this Code of Conduct, and will communicate reasons for moderation
|
||||
decisions when appropriate.
|
||||
|
||||
## Scope
|
||||
|
||||
This Code of Conduct applies within all community spaces, and also applies when
|
||||
an individual is officially representing the community in public spaces.
|
||||
Examples of representing our community include using an official e-mail address,
|
||||
posting via an official social media account, or acting as an appointed
|
||||
representative at an online or offline event.
|
||||
|
||||
## Enforcement
|
||||
|
||||
Instances of abusive, harassing, or otherwise unacceptable behavior may be
|
||||
reported to the community leaders responsible for enforcement at
|
||||
[tsc@loopback.io](mailto:tsc@loopback.io). All complaints will be reviewed and
|
||||
investigated promptly and fairly.
|
||||
|
||||
All community leaders are obligated to respect the privacy and security of the
|
||||
reporter of any incident.
|
||||
|
||||
## Enforcement Guidelines
|
||||
|
||||
Community leaders will follow these Community Impact Guidelines in determining
|
||||
the consequences for any action they deem in violation of this Code of Conduct:
|
||||
|
||||
### 1. Correction
|
||||
|
||||
**Community Impact**: Use of inappropriate language or other behavior deemed
|
||||
unprofessional or unwelcome in the community.
|
||||
|
||||
**Consequence**: A private, written warning from community leaders, providing
|
||||
clarity around the nature of the violation and an explanation of why the
|
||||
behavior was inappropriate. A public apology may be requested.
|
||||
|
||||
### 2. Warning
|
||||
|
||||
**Community Impact**: A violation through a single incident or series of
|
||||
actions.
|
||||
|
||||
**Consequence**: A warning with consequences for continued behavior. No
|
||||
interaction with the people involved, including unsolicited interaction with
|
||||
those enforcing the Code of Conduct, for a specified period of time. This
|
||||
includes avoiding interactions in community spaces as well as external channels
|
||||
like social media. Violating these terms may lead to a temporary or permanent
|
||||
ban.
|
||||
|
||||
### 3. Temporary Ban
|
||||
|
||||
**Community Impact**: A serious violation of community standards, including
|
||||
sustained inappropriate behavior.
|
||||
|
||||
**Consequence**: A temporary ban from any sort of interaction or public
|
||||
communication with the community for a specified period of time. No public or
|
||||
private interaction with the people involved, including unsolicited interaction
|
||||
with those enforcing the Code of Conduct, is allowed during this period.
|
||||
Violating these terms may lead to a permanent ban.
|
||||
|
||||
### 4. Permanent Ban
|
||||
|
||||
**Community Impact**: Demonstrating a pattern of violation of community
|
||||
standards, including sustained inappropriate behavior, harassment of an
|
||||
individual, or aggression toward or disparagement of classes of individuals.
|
||||
|
||||
**Consequence**: A permanent ban from any sort of public interaction within the
|
||||
community.
|
||||
|
||||
## Attribution
|
||||
|
||||
This Code of Conduct is adapted from the [Contributor Covenant][homepage],
|
||||
version 2.0, available at
|
||||
https://www.contributor-covenant.org/version/2/0/code_of_conduct.html.
|
||||
|
||||
Community Impact Guidelines were inspired by
|
||||
[Mozilla's code of conduct enforcement ladder](https://github.com/mozilla/diversity).
|
||||
|
||||
[homepage]: https://www.contributor-covenant.org
|
||||
|
||||
For answers to common questions about this code of conduct, see the FAQ at
|
||||
https://www.contributor-covenant.org/faq. Translations are available at
|
||||
https://www.contributor-covenant.org/translations.
|
25
README.md
25
README.md
|
@ -4,9 +4,13 @@ An ORM/ODM that provides a common set of interfaces for interacting with databas
|
|||
|
||||
## Supported versions
|
||||
|
||||
Current|Long Term Support
|
||||
:-:|:-:
|
||||
3.x|2.x
|
||||
This module adopts the [Module Long Term Support (LTS)](http://github.com/CloudNativeJS/ModuleLTS) policy, with the following End Of Life (EOL) dates:
|
||||
|
||||
| Version | Status | Published | EOL |
|
||||
| ---------- | --------------- | --------- | -------------------- |
|
||||
| 4.x | Current | Oct 2018 | Apr 2023 _(minimum)_ |
|
||||
| 3.x | End-of-Life | Dec 2016 | Dec 2020 |
|
||||
| 2.x | End-of-Life | Jul 2014 | Apr 2019 |
|
||||
|
||||
Learn more about our LTS plan in the [LoopBack documentation](http://loopback.io/doc/en/contrib/Long-term-support.html).
|
||||
|
||||
|
@ -29,3 +33,18 @@ npm install loopback-connector-mongodb // in this case, the mongodb connector
|
|||
See the [LoopBack documentation](http://loopback.io/doc/en/lb3/index.html).
|
||||
|
||||
For information on data source connectors, see [Connecting models to data sources](https://loopback.io/doc/en/lb3/Connecting-models-to-data-sources.html).
|
||||
|
||||
|
||||
## Contributing
|
||||
|
||||
This project uses [DCO](https://developercertificate.org/). Be sure to sign off
|
||||
your commits using the `-s` flag or adding `Signed-off-By: Name<Email>` in the
|
||||
commit message.
|
||||
|
||||
**Example**
|
||||
|
||||
```
|
||||
git commit -s -m "feat: my commit message"
|
||||
```
|
||||
|
||||
Also see the [Contributing to LoopBack](https://loopback.io/doc/en/contrib/code-contrib.html) to get you started.
|
||||
|
|
|
@ -0,0 +1,19 @@
|
|||
# Security Policy
|
||||
|
||||
## Security advisories
|
||||
|
||||
Security advisories can be found on the
|
||||
[LoopBack website](https://loopback.io/doc/en/sec/index.html).
|
||||
|
||||
## Reporting a vulnerability
|
||||
|
||||
If you think you have discovered a new security issue with any LoopBack package,
|
||||
**please do not report it on GitHub**. Instead, send an email to
|
||||
[security@loopback.io](mailto:security@loopback.io) with the following details:
|
||||
|
||||
- Full description of the vulnerability.
|
||||
- Steps to reproduce the issue.
|
||||
- Possible solutions.
|
||||
|
||||
If you are sending us any logs as part of the report, then make sure to redact
|
||||
any sensitive data from them.
|
|
@ -0,0 +1,21 @@
|
|||
// Copyright IBM Corp. 2017,2021. All Rights Reserved.
|
||||
// Node module: loopback-datasource-juggler
|
||||
// This file is licensed under the MIT License.
|
||||
// License text available at https://opensource.org/licenses/MIT
|
||||
|
||||
'use strict';
|
||||
|
||||
const isCI = process.env.CI;
|
||||
module.exports = {
|
||||
extends: [
|
||||
'@commitlint/config-conventional',
|
||||
],
|
||||
rules: {
|
||||
'header-max-length': [2, 'always', 100],
|
||||
'body-leading-blank': [2, 'always'],
|
||||
'footer-leading-blank': [0, 'always'],
|
||||
// Only enforce the rule if CI flag is not set. This is useful for release
|
||||
// commits to skip DCO
|
||||
'signed-off-by': [isCI ? 0 : 2, 'always', 'Signed-off-by:'],
|
||||
},
|
||||
};
|
|
@ -1,7 +1,12 @@
|
|||
{
|
||||
"content": [
|
||||
"lib/dao.js",
|
||||
"lib/scope.js",
|
||||
"lib/relation-definition.js",
|
||||
"lib/datasource.js",
|
||||
"lib/model.js",
|
||||
"lib/model-definition.js",
|
||||
"lib/mixins.js",
|
||||
"lib/date-string.js",
|
||||
"lib/geo.js",
|
||||
"lib/hooks.js",
|
||||
|
|
|
@ -350,7 +350,7 @@ source.
|
|||
* static and prototype methods to be mixed into the model constructor. The property can be defined
|
||||
* on the prototype.
|
||||
*/
|
||||
connector.DataAccessObject = function {};
|
||||
connector.DataAccessObject = function() {};
|
||||
|
||||
/**
|
||||
* Connector instance can have an optional function to be called to handle data model definitions.
|
||||
|
|
|
@ -178,11 +178,11 @@ There are a set of options to control the model definition.
|
|||
|
||||
- strict:
|
||||
- true: Only properties defined in the model are accepted. Use this
|
||||
mode if you want to make sure only predefined properties are accepted.
|
||||
mode if you want to make sure only predefined properties are accepted. Relational databases only support this setting.
|
||||
- false: The model will be an open model. All properties are accepted,
|
||||
including the ones that not predefined with the model. This mode is useful
|
||||
if the mobile application just wants to store free form JSON data to
|
||||
a schema-less database such as MongoDB.
|
||||
a schema-less database such as MongoDB. For relational databases, the value will be converted back to true.
|
||||
- undefined: Default to false unless the data source is backed by a
|
||||
relational database such as Oracle or MySQL.
|
||||
|
||||
|
|
|
@ -1,20 +1,20 @@
|
|||
// Copyright IBM Corp. 2013,2016. All Rights Reserved.
|
||||
// Copyright IBM Corp. 2013,2019. All Rights Reserved.
|
||||
// Node module: loopback-datasource-juggler
|
||||
// This file is licensed under the MIT License.
|
||||
// License text available at https://opensource.org/licenses/MIT
|
||||
|
||||
'use strict';
|
||||
|
||||
var DataSource = require('../../loopback-datasource-juggler').DataSource;
|
||||
var ModelBuilder = require('../../loopback-datasource-juggler').ModelBuilder;
|
||||
var introspectType = require('../lib/introspection')(ModelBuilder);
|
||||
const DataSource = require('../../loopback-datasource-juggler').DataSource;
|
||||
const ModelBuilder = require('../../loopback-datasource-juggler').ModelBuilder;
|
||||
const introspectType = require('../lib/introspection')(ModelBuilder);
|
||||
|
||||
var ds = new DataSource('memory');
|
||||
const ds = new DataSource('memory');
|
||||
|
||||
// Create a open model that doesn't require a schema
|
||||
var Application = ds.createModel('Schemaless', {}, {strict: false});
|
||||
const Application = ds.createModel('Schemaless', {}, {strict: false});
|
||||
|
||||
var application = {
|
||||
const application = {
|
||||
owner: 'rfeng',
|
||||
name: 'MyApp1',
|
||||
description: 'My first app',
|
||||
|
@ -47,7 +47,7 @@ Application.create(application, function(err, app1) {
|
|||
});
|
||||
|
||||
// Instance JSON document
|
||||
var user = {
|
||||
const user = {
|
||||
name: 'Joe',
|
||||
age: 30,
|
||||
birthday: new Date(),
|
||||
|
@ -68,13 +68,13 @@ var user = {
|
|||
};
|
||||
|
||||
// Introspect the JSON document to generate a schema
|
||||
var schema = introspectType(user);
|
||||
const schema = introspectType(user);
|
||||
|
||||
// Create a model for the generated schema
|
||||
var User = ds.createModel('User', schema, {idInjection: true});
|
||||
const User = ds.createModel('User', schema, {idInjection: true});
|
||||
|
||||
// Use the model for CRUD
|
||||
var obj = new User(user);
|
||||
const obj = new User(user);
|
||||
|
||||
console.log(obj.toObject());
|
||||
|
||||
|
|
|
@ -1,14 +1,14 @@
|
|||
// Copyright IBM Corp. 2013,2016. All Rights Reserved.
|
||||
// Copyright IBM Corp. 2013,2019. All Rights Reserved.
|
||||
// Node module: loopback-datasource-juggler
|
||||
// This file is licensed under the MIT License.
|
||||
// License text available at https://opensource.org/licenses/MIT
|
||||
|
||||
'use strict';
|
||||
|
||||
var ModelBuilder = require('../../loopback-datasource-juggler').ModelBuilder;
|
||||
var modelBuilder = new ModelBuilder();
|
||||
const ModelBuilder = require('../../loopback-datasource-juggler').ModelBuilder;
|
||||
const modelBuilder = new ModelBuilder();
|
||||
// define models
|
||||
var Post = modelBuilder.define('Post', {
|
||||
const Post = modelBuilder.define('Post', {
|
||||
title: {type: String, length: 255},
|
||||
content: {type: ModelBuilder.Text},
|
||||
date: {type: Date, default: function() {
|
||||
|
@ -19,7 +19,7 @@ var Post = modelBuilder.define('Post', {
|
|||
});
|
||||
|
||||
// simpler way to describe model
|
||||
var User = modelBuilder.define('User', {
|
||||
const User = modelBuilder.define('User', {
|
||||
name: String,
|
||||
bio: ModelBuilder.Text,
|
||||
approved: Boolean,
|
||||
|
@ -27,14 +27,14 @@ var User = modelBuilder.define('User', {
|
|||
age: Number,
|
||||
});
|
||||
|
||||
var Group = modelBuilder.define('Group', {group: String});
|
||||
const Group = modelBuilder.define('Group', {group: String});
|
||||
|
||||
// define any custom method
|
||||
User.prototype.getNameAndAge = function() {
|
||||
return this.name + ', ' + this.age;
|
||||
};
|
||||
|
||||
var user = new User({name: 'Joe'});
|
||||
let user = new User({name: 'Joe'});
|
||||
console.log(user);
|
||||
|
||||
console.log(modelBuilder.models);
|
||||
|
|
|
@ -1,16 +1,16 @@
|
|||
// Copyright IBM Corp. 2013,2016. All Rights Reserved.
|
||||
// Copyright IBM Corp. 2013,2019. All Rights Reserved.
|
||||
// Node module: loopback-datasource-juggler
|
||||
// This file is licensed under the MIT License.
|
||||
// License text available at https://opensource.org/licenses/MIT
|
||||
|
||||
'use strict';
|
||||
|
||||
var DataSource = require('../../loopback-datasource-juggler').DataSource;
|
||||
var ModelBuilder = require('../../loopback-datasource-juggler').ModelBuilder;
|
||||
var ds = new DataSource('memory');
|
||||
const DataSource = require('../../loopback-datasource-juggler').DataSource;
|
||||
const ModelBuilder = require('../../loopback-datasource-juggler').ModelBuilder;
|
||||
const ds = new DataSource('memory');
|
||||
|
||||
// define models
|
||||
var Post = ds.define('Post', {
|
||||
const Post = ds.define('Post', {
|
||||
title: {type: String, length: 255},
|
||||
content: {type: DataSource.Text},
|
||||
date: {type: Date, default: function() {
|
||||
|
@ -21,7 +21,7 @@ var Post = ds.define('Post', {
|
|||
});
|
||||
|
||||
// simplier way to describe model
|
||||
var User = ds.define('User', {
|
||||
const User = ds.define('User', {
|
||||
name: String,
|
||||
bio: DataSource.Text,
|
||||
approved: Boolean,
|
||||
|
@ -29,14 +29,14 @@ var User = ds.define('User', {
|
|||
age: Number,
|
||||
});
|
||||
|
||||
var Group = ds.define('Group', {name: String});
|
||||
const Group = ds.define('Group', {name: String});
|
||||
|
||||
// define any custom method
|
||||
User.prototype.getNameAndAge = function() {
|
||||
return this.name + ', ' + this.age;
|
||||
};
|
||||
|
||||
var user = new User({name: 'Joe'});
|
||||
const user = new User({name: 'Joe'});
|
||||
console.log(user);
|
||||
|
||||
// console.log(ds.models);
|
||||
|
@ -58,10 +58,10 @@ Post.belongsTo(User, {as: 'author', foreignKey: 'userId'});
|
|||
|
||||
User.hasAndBelongsToMany('groups');
|
||||
|
||||
var user2 = new User({name: 'Smith', age: 14});
|
||||
const user2 = new User({name: 'Smith', age: 14});
|
||||
user2.save(function(err) {
|
||||
console.log(user2);
|
||||
var post = user2.posts.build({title: 'Hello world'});
|
||||
const post = user2.posts.build({title: 'Hello world'});
|
||||
post.save(function(err, data) {
|
||||
console.log(err ? err : data);
|
||||
});
|
||||
|
@ -77,7 +77,7 @@ User.create({name: 'Jeff', age: 12}, function(err, data) {
|
|||
return;
|
||||
}
|
||||
console.log(data);
|
||||
var post = data.posts.build({title: 'My Post'});
|
||||
const post = data.posts.build({title: 'My Post'});
|
||||
console.log(post);
|
||||
});
|
||||
|
||||
|
@ -90,8 +90,8 @@ User.minors(function(err, kids) {
|
|||
console.log('Kids: ', kids);
|
||||
});
|
||||
|
||||
var Article = ds.define('Article', {title: String});
|
||||
var Tag = ds.define('Tag', {name: String});
|
||||
const Article = ds.define('Article', {title: String});
|
||||
const Tag = ds.define('Tag', {name: String});
|
||||
Article.hasAndBelongsToMany('tags');
|
||||
|
||||
Article.create(function(e, article) {
|
||||
|
@ -105,7 +105,7 @@ Article.create(function(e, article) {
|
|||
});
|
||||
|
||||
// should be able to attach a data source to an existing model
|
||||
var modelBuilder = new ModelBuilder();
|
||||
const modelBuilder = new ModelBuilder();
|
||||
|
||||
const Color = modelBuilder.define('Color', {
|
||||
name: String,
|
||||
|
|
|
@ -1,14 +1,14 @@
|
|||
// Copyright IBM Corp. 2014,2016. All Rights Reserved.
|
||||
// Copyright IBM Corp. 2014,2019. All Rights Reserved.
|
||||
// Node module: loopback-datasource-juggler
|
||||
// This file is licensed under the MIT License.
|
||||
// License text available at https://opensource.org/licenses/MIT
|
||||
|
||||
'use strict';
|
||||
|
||||
var jdb = require('../index');
|
||||
const jdb = require('../index');
|
||||
|
||||
var User, Post, Passport, City, Street, Building;
|
||||
var nbSchemaRequests = 0;
|
||||
let User, Post, Passport, City, Street, Building;
|
||||
const nbSchemaRequests = 0;
|
||||
|
||||
setup(function() {
|
||||
Passport.find({include: 'owner'}, function(err, passports) {
|
||||
|
@ -35,7 +35,7 @@ setup(function() {
|
|||
});
|
||||
|
||||
function setup(done) {
|
||||
var db = new jdb.DataSource({connector: 'memory'});
|
||||
const db = new jdb.DataSource({connector: 'memory'});
|
||||
City = db.define('City');
|
||||
Street = db.define('Street');
|
||||
Building = db.define('Building');
|
||||
|
@ -56,9 +56,9 @@ function setup(done) {
|
|||
Post.belongsTo('author', {model: User, foreignKey: 'userId'});
|
||||
|
||||
db.automigrate(function() {
|
||||
var createdUsers = [];
|
||||
var createdPassports = [];
|
||||
var createdPosts = [];
|
||||
let createdUsers = [];
|
||||
let createdPassports = [];
|
||||
let createdPosts = [];
|
||||
createUsers();
|
||||
function createUsers() {
|
||||
clearAndCreate(
|
||||
|
@ -73,7 +73,7 @@ function setup(done) {
|
|||
function(items) {
|
||||
createdUsers = items;
|
||||
createPassports();
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -88,7 +88,7 @@ function setup(done) {
|
|||
function(items) {
|
||||
createdPassports = items;
|
||||
createPosts();
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -105,19 +105,19 @@ function setup(done) {
|
|||
function(items) {
|
||||
createdPosts = items;
|
||||
done();
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function clearAndCreate(model, data, callback) {
|
||||
var createdItems = [];
|
||||
const createdItems = [];
|
||||
model.destroyAll(function() {
|
||||
nextItem(null, null);
|
||||
});
|
||||
|
||||
var itemIndex = 0;
|
||||
let itemIndex = 0;
|
||||
|
||||
function nextItem(err, lastItem) {
|
||||
if (lastItem !== null) {
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
// Copyright IBM Corp. 2013,2016. All Rights Reserved.
|
||||
// Copyright IBM Corp. 2013,2019. All Rights Reserved.
|
||||
// Node module: loopback-datasource-juggler
|
||||
// This file is licensed under the MIT License.
|
||||
// License text available at https://opensource.org/licenses/MIT
|
||||
|
||||
'use strict';
|
||||
|
||||
var path = require('path'),
|
||||
const path = require('path'),
|
||||
fs = require('fs'),
|
||||
DataSource = require('../lib/datasource').DataSource;
|
||||
|
||||
|
@ -21,12 +21,12 @@ function loadSchemasSync(schemaFile, dataSource) {
|
|||
}
|
||||
|
||||
// Read the dataSource JSON file
|
||||
var schemas = JSON.parse(fs.readFileSync(schemaFile));
|
||||
const schemas = JSON.parse(fs.readFileSync(schemaFile));
|
||||
|
||||
return dataSource.buildModels(schemas);
|
||||
}
|
||||
|
||||
var models = loadSchemasSync(path.join(__dirname, 'jdb-schemas.json'));
|
||||
let models = loadSchemasSync(path.join(__dirname, 'jdb-schemas.json'));
|
||||
|
||||
for (const s in models) {
|
||||
const m = models[s];
|
||||
|
|
|
@ -1,15 +1,15 @@
|
|||
// Copyright IBM Corp. 2013,2016. All Rights Reserved.
|
||||
// Copyright IBM Corp. 2013,2019. All Rights Reserved.
|
||||
// Node module: loopback-datasource-juggler
|
||||
// This file is licensed under the MIT License.
|
||||
// License text available at https://opensource.org/licenses/MIT
|
||||
|
||||
'use strict';
|
||||
|
||||
var ModelBuilder = require('../../loopback-datasource-juggler').ModelBuilder;
|
||||
var modelBuilder = new ModelBuilder();
|
||||
const ModelBuilder = require('../../loopback-datasource-juggler').ModelBuilder;
|
||||
const modelBuilder = new ModelBuilder();
|
||||
|
||||
// simplier way to describe model
|
||||
var User = modelBuilder.define('User', {
|
||||
const User = modelBuilder.define('User', {
|
||||
name: String,
|
||||
bio: ModelBuilder.Text,
|
||||
approved: Boolean,
|
||||
|
@ -31,7 +31,7 @@ var User = modelBuilder.define('User', {
|
|||
friends: [String],
|
||||
});
|
||||
|
||||
var user = new User({
|
||||
const user = new User({
|
||||
name: 'Joe',
|
||||
age: 20,
|
||||
address: {street: '123 Main St', 'city': 'San Jose', state: 'CA'},
|
||||
|
|
|
@ -1,26 +1,26 @@
|
|||
// Copyright IBM Corp. 2013,2016. All Rights Reserved.
|
||||
// Copyright IBM Corp. 2013,2019. All Rights Reserved.
|
||||
// Node module: loopback-datasource-juggler
|
||||
// This file is licensed under the MIT License.
|
||||
// License text available at https://opensource.org/licenses/MIT
|
||||
|
||||
'use strict';
|
||||
|
||||
var DataSource = require('../index').DataSource;
|
||||
var ds = new DataSource('memory');
|
||||
const DataSource = require('../index').DataSource;
|
||||
const ds = new DataSource('memory');
|
||||
|
||||
var Order = ds.createModel('Order', {
|
||||
const Order = ds.createModel('Order', {
|
||||
items: [String],
|
||||
orderDate: Date,
|
||||
qty: Number,
|
||||
});
|
||||
|
||||
var Customer = ds.createModel('Customer', {
|
||||
const Customer = ds.createModel('Customer', {
|
||||
name: String,
|
||||
});
|
||||
|
||||
Order.belongsTo(Customer);
|
||||
|
||||
var order1, order2, order3;
|
||||
let order1, order2, order3;
|
||||
|
||||
Customer.create({name: 'John'}, function(err, customer) {
|
||||
Order.create({customerId: customer.id, orderDate: new Date(), items: ['Book']}, function(err, order) {
|
||||
|
@ -42,7 +42,7 @@ Customer.create({name: 'John'}, function(err, customer) {
|
|||
});
|
||||
});
|
||||
|
||||
var customer3 = order.customer.build({name: 'Tom'});
|
||||
const customer3 = order.customer.build({name: 'Tom'});
|
||||
console.log('Customer 3', customer3);
|
||||
});
|
||||
});
|
||||
|
@ -67,15 +67,15 @@ Customer.create({name: 'Ray'}, function(err, customer) {
|
|||
});
|
||||
});
|
||||
|
||||
var Physician = ds.createModel('Physician', {
|
||||
const Physician = ds.createModel('Physician', {
|
||||
name: String,
|
||||
});
|
||||
|
||||
var Patient = ds.createModel('Patient', {
|
||||
const Patient = ds.createModel('Patient', {
|
||||
name: String,
|
||||
});
|
||||
|
||||
var Appointment = ds.createModel('Appointment', {
|
||||
const Appointment = ds.createModel('Appointment', {
|
||||
physicianId: Number,
|
||||
patientId: Number,
|
||||
appointmentDate: Date,
|
||||
|
@ -102,25 +102,27 @@ Physician.create({name: 'Dr John'}, function(err, physician1) {
|
|||
patient1.physicians(console.log);
|
||||
|
||||
// Build an appointment?
|
||||
var patient3 = patient1.physicians.build({name: 'Dr X'});
|
||||
const patient3 = patient1.physicians.build({name: 'Dr X'});
|
||||
console.log('Physician 3: ', patient3, patient3.constructor.modelName);
|
||||
|
||||
// Create a physician?
|
||||
patient1.physicians.create({name: 'Dr X'}, function(err, patient4) {
|
||||
console.log('Physician 4: ', patient4, patient4.constructor.modelName);
|
||||
});
|
||||
});
|
||||
});
|
||||
},
|
||||
);
|
||||
},
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
var Assembly = ds.createModel('Assembly', {
|
||||
const Assembly = ds.createModel('Assembly', {
|
||||
name: String,
|
||||
});
|
||||
|
||||
var Part = ds.createModel('Part', {
|
||||
const Part = ds.createModel('Part', {
|
||||
partNumber: String,
|
||||
});
|
||||
|
||||
|
@ -135,7 +137,7 @@ Assembly.create({name: 'car'}, function(err, assembly) {
|
|||
});
|
||||
|
||||
// Build an part?
|
||||
var part3 = assembly.parts.build({partNumber: 'door'});
|
||||
const part3 = assembly.parts.build({partNumber: 'door'});
|
||||
console.log('Part3: ', part3, part3.constructor.modelName);
|
||||
|
||||
// Create a part?
|
||||
|
|
7
index.js
7
index.js
|
@ -1,10 +1,11 @@
|
|||
// Copyright IBM Corp. 2011,2016. All Rights Reserved.
|
||||
// Copyright IBM Corp. 2011,2019. All Rights Reserved.
|
||||
// Node module: loopback-datasource-juggler
|
||||
// This file is licensed under the MIT License.
|
||||
// License text available at https://opensource.org/licenses/MIT
|
||||
|
||||
'use strict';
|
||||
|
||||
var SG = require('strong-globalize');
|
||||
const SG = require('strong-globalize');
|
||||
SG.SetRootDir(__dirname);
|
||||
|
||||
exports.ModelBuilder = exports.LDL = require('./lib/model-builder.js').ModelBuilder;
|
||||
|
@ -17,7 +18,7 @@ Object.defineProperty(exports, 'version', {
|
|||
get: function() { return require('./package.json').version; },
|
||||
});
|
||||
|
||||
var commonTest = './test/common_test';
|
||||
const commonTest = './test/common_test';
|
||||
Object.defineProperty(exports, 'test', {
|
||||
get: function() { return require(commonTest); },
|
||||
});
|
||||
|
|
|
@ -0,0 +1,97 @@
|
|||
{
|
||||
"0483a77cf77741504204e5c066597487": "Vztah {{polymorphic}} {0}: {1} neočekává parametr `polymorphic.as` při definování vlastního parametru `foreignKey`/`discriminator` ",
|
||||
"09483e03b91c8bd58732a74b3ef1ec13": "Neplatné datum: {0}",
|
||||
"0a5aa17f7866a85e3aee37ef5369403c": "LinkManyToMany přijal cíl, který neobsahuje povinný \"{0}\"",
|
||||
"0b16d3ffc42f91b4b9a4b3b50c41c838": "Pořadí {0} není platné",
|
||||
"0bd753a8944ad0af85a939bb25273887": "Nelze ukončit platnost neznámého klíče {0}",
|
||||
"0c0b867aca0973ba26e887d3337cc4ec": "Model {{Polymorphic}} nebyl nalezen: `{0}` nenastaven",
|
||||
"0c4eb8b6c2ff6e51d7e195eee346ced9": "Tabulka '{0}' neexistuje.",
|
||||
"0ff31abb394afb555df162e74ff1a0a0": "{{id}} nelze aktualizovat z {0} na {1}, když je {{forceId}} nastaveno na true",
|
||||
"1ae7d3e0be381efb32bfd1ba652f5172": "VAROVÁNÍ: Vztah {{polymorphic}} {0}: {1} používá klíčové slovo `polymorphic.as`, které bude ZAMÍTNUTO v LoopBack.next, vit tato dokumentace s řešeními nahrazení (https://loopback.io/doc/en/lb3/Polymorphic-relations.html#deprecated-polymorphic-as)",
|
||||
"1daef4e937fe52136597ba8fd2060f55": "Vnoření transakcí není podporováno",
|
||||
"21095484501dbff31af6556fa6039182": "Parametr {{offset/skip}} {0} není platný",
|
||||
"280f4550f90e133118955ec6f6f72830": "Byl zadán typ diskriminátoru {0}, ale neexistuje žádný model s tímto názvem",
|
||||
"28697ec15968a7969211f6d035ba9260": "Vztah {{polymorphic}} {0}: {1} neočekává parametr `model`",
|
||||
"2c4904377a87fdab502118719cc0d266": "{{Transaction}} není podporována",
|
||||
"2c5c8519721f749aab13c2f04f41d611": "Vlastnost {0} má neplatnou klauzuli {1}: Očekávaly se přesně 2 hodnoty, přijaty {2}",
|
||||
"2f4af31c144bbfab1bbf479866acd820": "\nVAROVÁNÍ: Konektor {{LoopBack}} \"{0}\" není nainstalován jako žádný z následujících modulů:\n\n {1}\n\nChcete-li provést opravu, spusťte:\n\n {{npm install {2} --save}}\n",
|
||||
"3864f9be10f27723074566d2b3893514": "Varování: Model {0}, {{strict mode: `throw`}} byl odebrán, místo toho použijte {{`strict: true`}}, což vrací {{`Validation Error`}} pro neznámé vlastnosti,",
|
||||
"38dbf42c29a4645238cc3d632e88ebc9": "{{Relation.modelTo}} není definován pro vztah {0} a není {{polymorphic}}",
|
||||
"3cde8cc9bca22c67278b202ab0720106": "Žádná instance s ID {0} nebyla nalezena pro {1}",
|
||||
"416dfbb7b823f51c9f3800be81060b41": "Žádná instance s {{id}} {0} nebyla nalezena pro {1}",
|
||||
"49b5afd8c6a19ad9c8abeffb2f8114eb": "Metoda BelongsTo \"getAsync()\" je zamítnuta, místo toho použijte \"get()\".",
|
||||
"4c78325cedbb826db3a05bf5df0e8546": "Musíte zadat {{id}} při nahrazení!",
|
||||
"4e31b1edd10dadb724d83387de0b5062": "Parametr {{limit}} {0} není platný",
|
||||
"514985b2327f061ffb1c932f6b909979": "Model {0} není definován.",
|
||||
"525c856e65daab43be247e7b5410febd": "Vztah {{polymorphic}} {0}: {1} neočekává parametr `polymorphic.selector` při definování vlastního parametru `foreignKey`/`discriminator` ",
|
||||
"5c18ee111dd87540cdb19a2a93b33be9": "Transakce je odvolána v důsledku vypršení časového limitu",
|
||||
"5ec7e6664256f7ea78f4f06dafc7d974": "Transakce není připravena, počkejte na vyřešení vráceného závazku",
|
||||
"5ec8efeb715a2c34b440f2d76e2cf87d": " {0}",
|
||||
"6111399276924ffa3bc9a410cdfcb2e5": "Žádný název {{id}} {0}",
|
||||
"614e3355647e4127c96256102dc63376": "Vlastnost {0} má neplatnou klauzuli {1}: Byl očekáván řetězec nebo RegExp",
|
||||
"62a2d80c405b7fec5f547c448ab1b6ff": "{{order}} {0} má neplatný směr",
|
||||
"6502a117987610380b9068ef98b1b0ee": "Nebyl nalezen žádný záznam v {0} pro ({1}. {2}, {3}. {4})",
|
||||
"67c2bf43b5281ab929617423ea8a6f3e": "Konektor {0} nepodporuje operaci {{replaceById}}. Nejde o chybu ve zpětné smyčce. Obraťte se na autory konektoru, pokud možno přes problémy GitHub.",
|
||||
"6c3234937d69763fc7f6bcafccc59bbc": "{{Model::deleteById}} vyžaduje argument {{id}}",
|
||||
"6eb6fd4fbd73394000bc25f5776fd20c": "{{Model::exists}} vyžaduje argument {{id}}",
|
||||
"6fcc2ff0db7a4f490f5e0ce9e24691f3": "Vztah {{HasOne}} nemůže vytvořit více než jednu instanci {0}",
|
||||
"728232e473bf80272c042df2b7e002f4": "Vztah {{polymorphic}} {0}: {1} vyžaduje parametr `polymorphic.discriminator`, je-li dodán parametr `polymorphic.foreignKey`",
|
||||
"791ab3031a73ede03f7d6299a85e8289": "Časový limit připojení po {0} ms",
|
||||
"7b277018e43d41bc445731092b91547d": "Nepřipojeno",
|
||||
"7bbbdece4eea90e42aa5c0bce295e503": "{{Model::findById}} vyžaduje argument {{id}}",
|
||||
"7e9530c0399289be0ee601a604be71ff": "Vztah {{BelongsTo}} {0} je prázdný",
|
||||
"7faa840eb6ce11250a141deb42a6c489": "Neznámý vztah {{scope}}: {0}",
|
||||
"8091838319a5cc7d6a34af2f2a616ce9": "Název vlastnosti by neměl být \"{{constructor}}\" v modelu: {0}",
|
||||
"881e4b0cb86ed59549248ee540a9fd10": "Název vlastnosti \"{{constructor}}\" není povolen v datech {0}",
|
||||
"89afd3a9249f5a8d3edda07d84ca049d": "Model {{Polymorphic}} nebyl nalezen: `{0}`",
|
||||
"89bf6d92731fe7bd2146ce8d0bec205c": "Neplatný argument, musí být řetězec, {{regex}} nebo {{RegExp}}",
|
||||
"8a39126103a157f501affa070367a1b0": "Instance {0} není platná. Podrobnosti: {1}.",
|
||||
"8c5ab01638c1ac1d58168c6346a8481a": "Neplatné příznaky {{regex}}: {0}",
|
||||
"938401ea4ce48159efa9be1d4a5e8bab": "Položkami musí být pole: {0}",
|
||||
"9e1f143ee02946324d34da92f71bf74e": "Vztah {0}: {1} vyžaduje parametr `model`",
|
||||
"a004f310d315e592843776fab964eaeb": "Vztahy {{Polymorphic}} vyžadují model typu through",
|
||||
"a0cf0e09c26df14283223e84e6a10f00": "Nelze aktualizovat atributy. {{Object}} s {{id}} {0} neexistuje!",
|
||||
"a2487abefef4259c2131d96cdb8543b1": "Připojení se nezdařilo: {0} \nBude zopakováno pro další požadavek.",
|
||||
"a25e41a39c60c4702e55d0c3936576a1": "Neshoda klíče: {0}. {1}: {2}, {3}. {4}: {5}",
|
||||
"a327355560d495454fba2c1aad6bdf09": "Neznámá metoda rozsahu: {0}",
|
||||
"a6c18a7f4390cd3d59a2a7a047ae2aab": "Spustit příkaz \"{{npm install loopback-datasource-juggler}} {0}\" ",
|
||||
"a829dee089c912e68c18920ba015400c": "VAROVÁNÍ: Vlastnost {{id}} nelze změnit z {0} na {1} pro model: {2} v háčku operace {{'loaded'}}",
|
||||
"a984a076c59e451948b2bcf7a393d860": "VAROVÁNÍ: Vlastnost {{id}} nelze změnit z {0} na {1} pro model: {2} v háčku operace {{'before save'}}",
|
||||
"ac04cf275b71c1eb89a41cf6bbad7a64": "Metoda HasOne \"getAsync()\" je zamítnuta, místo toho použijte \"get()\".",
|
||||
"b138294f132edfe1eb2a8211150c7238": "Neočekávaný parametr `undefined` v dotazu",
|
||||
"b15b20280211ad258d92947f05b6e4a5": "Konektor nebyl inicializován.",
|
||||
"b278876ec93ef9760f00e83f38ba313d": "Metoda Scope \"getAsync()\" je zamítnuta, místo toho použijte \"find()\".",
|
||||
"ba0fd8106eb54de4d003a844206431fd": "Háček modelu \"{0}\" je zamítnut, místo toho použijte háčky operace. {{http://docs.strongloop.com/display/LB/Operation+hooks}}",
|
||||
"baf2c8b0c5a574b8a894e9b6304fece1": "Klauzule where {0} není {{object}}",
|
||||
"bdb11cc1c780c9ccac33c316cfdc9d82": "Není definován typ vlastnosti {0}.{1}",
|
||||
"bdfb951c8ff7ce0cbc08c06f548fd927": "Hodnota je prázdný {{object}}",
|
||||
"bec226891a505828bfc76c5cfd73b336": "Nelze získat TTL pro neznámý klíč {0}",
|
||||
"cd930369e86cdd222f7bd117c6f9fa94": "Neznámý poskytovatel výchozí hodnoty {0}",
|
||||
"cfee4d8149316d9a647c0885cf3cafaf": "Názvy vlastnosti obsahující tečku(y) nejsou podporovány. Model: {0}, dynamická vlastnost: {1}",
|
||||
"d40328eabd8756d795bcdd49d782d4e9": "Zdroj dat nepodporuje transakce",
|
||||
"da02dd6c53d4148320eeb31718a7aebe": "Neplatný typ pro vlastnost {0}",
|
||||
"da751a8a748adbde5b55fa83b707b4e2": "Názvy vlastnosti obsahující tečku(y) nejsou podporovány. Model: {0}, vlastnost: {1}",
|
||||
"db03083e9a768388fdbee865249ac67a": "Ignorují se chyby ověření platnosti v {{updateOrCreate()}}:",
|
||||
"dd63416d9b7d9fa4181e89efd619dfd8": "Hodnota není {{array}} nebo {{object}} se sekvenčními číselnými indexy",
|
||||
"ddf0aa14803f1c84f4a97f3803f7471c": "Je vyžadován název třídy",
|
||||
"e08ab0e1ab55f26c357061447b635905": "Nebyl nalezen žádný vztah v {0} pro ({1}. {2}, {3}. {4})",
|
||||
"e0e9504e137a3c3339144b51ed76fef2": "Konektor není definován správně: měl by vytvořit člen `{{connector}}` zdroje dat",
|
||||
"e2f282cbe3efba001d6d3a09f7f6ca8c": "Vztah {{polymorphic}} {0}: {1} vyžaduje parametr `polymorphic.foreignKey`, když je dodán parametr `polymorphic.discriminator`",
|
||||
"e39e0f5d52bfbf511e645d19ecadd2fa": "Vlastnost {0} má neplatnou klauzuli {1}: {2}",
|
||||
"e4434de4bb8f5a3cd1d416e4d80d7e0b": "Neznámé \"{0}\" {{id}} \"{1}\".",
|
||||
"e54d944c2a2c85a23caa86027ae307cf": "Nelze migrovat modely, které nejsou připojeny k tomuto zdroji dat: {0}",
|
||||
"e54f118a75e15e132f16b985274eb46d": "Filtr dotazu {0} není {{object}}",
|
||||
"e55937649d8d7a11706b8cec22d02eae": "Vztah {{HasOne}} {0} je prázdný",
|
||||
"e6161ae8459c79d810e2aa9d21282a39": "Při aktualizaci atributů musíte poskytnout {{id}}!",
|
||||
"eb56c2b0c30cf006e2df00a549ec9c2c": "Vztah \"{0}\" není definován pro model {1}",
|
||||
"ec42dca074f1818c447f7ad16e2d01af": "{0} není poskytnut připojeným konektorem",
|
||||
"ecb7aa804bf54c682999d20d6436104c": "{{transaction}} není aktivní: {0}",
|
||||
"f30809cb932b72a66416a709c8531530": "Konektor nepodporuje {{method}} v rámci transakce",
|
||||
"f41bd91dc0f000a79c0bf842f1b7fdf9": "nelze vytvořit seznam z řetězce JSON: {0}",
|
||||
"f6e8c96c93b9c7687d6c172b3695e898": "Vlastnost {{id}} ({0}) nelze aktualizovat z {1} na {2}",
|
||||
"fa9ae17e8e008d0eb0f0421a2972308c": "Vztah {{polymorphic}} {0}: {1} vyžaduje parametr `model`",
|
||||
"fca4d12faff1035d9d0438d73432571b": "Duplicitní položka pro {0}.{1}",
|
||||
"fd3cc89dc67e2d604eaae21bdf41d403": "Nelze najít vztah {0} pro model {1}",
|
||||
"fec8ebda24db46a9d040bf863765cc44": "Operátor {0} má neplatné klauzule {1}: {2}"
|
||||
}
|
||||
|
|
@ -0,0 +1,97 @@
|
|||
{
|
||||
"0483a77cf77741504204e5c066597487": "Relacja {{polymorphic}} {0}: {1} nie oczekuje parametru `polymorphic.as` podczas definiowania niestandardowego obiektu `foreignKey`/`discriminator` ",
|
||||
"09483e03b91c8bd58732a74b3ef1ec13": "Niepoprawna data: {0}",
|
||||
"0a5aa17f7866a85e3aee37ef5369403c": "Funkcja LinkManyToMany otrzymała element docelowy, który nie zawiera wymaganej wartości \"{0}\"",
|
||||
"0b16d3ffc42f91b4b9a4b3b50c41c838": "Kolejność {0} jest niepoprawna",
|
||||
"0bd753a8944ad0af85a939bb25273887": "Nie można unieważnić nieznanego klucza {0}",
|
||||
"0c0b867aca0973ba26e887d3337cc4ec": "Model {{Polymorphic}} nie został znaleziony: nie ustawiono właściwości `{0}`",
|
||||
"0c4eb8b6c2ff6e51d7e195eee346ced9": "Tabela '{0}' nie istnieje.",
|
||||
"0ff31abb394afb555df162e74ff1a0a0": "{{id}} nie może być zaktualizowany z {0} na {1}, gdy właściwość {{forceId}} ma wartość true",
|
||||
"1ae7d3e0be381efb32bfd1ba652f5172": "OSTRZEŻENIE: Relacja {{polymorphic}} {0}: {1} używa słowa kluczowego `polymorphic.as`, które będzie NIEAKTUALNE w aplikacji LoopBack.next; aby uzyskać informacje o rozwiązaniach zastępczych, zapoznaj się z tym dokumentem (https://loopback.io/doc/en/lb3/Polymorphic-relations.html#deprecated-polymorphic-as)",
|
||||
"1daef4e937fe52136597ba8fd2060f55": "Zagnieżdżanie transakcji nie jest obsługiwane",
|
||||
"21095484501dbff31af6556fa6039182": "Parametr {{offset/skip}} {0} jest niepoprawny",
|
||||
"280f4550f90e133118955ec6f6f72830": "Określono typ wyróżnika {0}, ale nie istnieje model o takiej nazwie",
|
||||
"28697ec15968a7969211f6d035ba9260": "Relacja {{polymorphic}} {0}: {1} nie oczekuje parametru `model`",
|
||||
"2c4904377a87fdab502118719cc0d266": "{{Transaction}} nie jest obsługiwana",
|
||||
"2c5c8519721f749aab13c2f04f41d611": "Właściwość {0} ma niepoprawną klauzulę {1}: oczekiwano dokładnie 2 wartości, otrzymano {2}",
|
||||
"2f4af31c144bbfab1bbf479866acd820": "\nOSTRZEŻENIE: Konektor {{LoopBack}} \"{0}\" nie jest zainstalowany jako żaden z następujących modułów:\n\n {1}\n\nAby to naprawić, uruchom komendę:\n\n {{npm install {2} --save}}\n",
|
||||
"3864f9be10f27723074566d2b3893514": "Ostrzeżenie: model {0}, {{strict mode: `throw`}}, został usunięty, zamiast niego użyj modelu {{`strict: true`}}, który zwraca {{`Validation Error`}} dla nieznanych właściwości,",
|
||||
"38dbf42c29a4645238cc3d632e88ebc9": "Model {{Relation.modelTo}} nie został zdefiniowany dla relacji {0} i nie jest {{polymorphic}}",
|
||||
"3cde8cc9bca22c67278b202ab0720106": "Nie znaleziono instancji o identyfikatorze {0} dla {1}",
|
||||
"416dfbb7b823f51c9f3800be81060b41": "Nie znaleziono instancji o identyfikatorze {{id}} {0} dla {1}",
|
||||
"49b5afd8c6a19ad9c8abeffb2f8114eb": "Metoda BelongsTo \"getAsync()\" jest nieaktualna, zamiast niej użyj metody \"get()\".",
|
||||
"4c78325cedbb826db3a05bf5df0e8546": "Podczas zastępowania należy podać {{id}}!",
|
||||
"4e31b1edd10dadb724d83387de0b5062": "Parametr {{limit}} {0} jest niepoprawny",
|
||||
"514985b2327f061ffb1c932f6b909979": "Model {0} nie jest zdefiniowany.",
|
||||
"525c856e65daab43be247e7b5410febd": "Relacja {{polymorphic}} {0}: {1} nie oczekuje parametru `polymorphic.selector` podczas definiowania niestandardowego obiektu `foreignKey`/`discriminator` ",
|
||||
"5c18ee111dd87540cdb19a2a93b33be9": "Transakcja została wycofana z powodu przekroczenia limitu czasu",
|
||||
"5ec7e6664256f7ea78f4f06dafc7d974": "Transakcja nie jest gotowa, poczekaj na rozstrzygnięcie zwróconej obietnicy",
|
||||
"5ec8efeb715a2c34b440f2d76e2cf87d": " {0}",
|
||||
"6111399276924ffa3bc9a410cdfcb2e5": "Brak nazwy {{id}} {0}",
|
||||
"614e3355647e4127c96256102dc63376": "Właściwość {0} ma niepoprawną klauzulę {1}: oczekiwano łańcucha lub wyrażenia regularnego",
|
||||
"62a2d80c405b7fec5f547c448ab1b6ff": "{{order}} {0} ma niepoprawny kierunek",
|
||||
"6502a117987610380b9068ef98b1b0ee": "Nie znaleziono rekordu w {0} dla ({1}.{2}, {3}.{4})",
|
||||
"67c2bf43b5281ab929617423ea8a6f3e": "Konektor {0} nie obsługuje operacji {{replaceById}}. To nie jest błąd aplikacji LoopBack. Skontaktuj się z autorami konektora, najlepiej za pośrednictwem sekcji serwisu GitHub poświęconej problemom.",
|
||||
"6c3234937d69763fc7f6bcafccc59bbc": "{{Model::deleteById}} wymaga argumentu {{id}}",
|
||||
"6eb6fd4fbd73394000bc25f5776fd20c": "{{Model::exists}} wymaga argumentu {{id}}",
|
||||
"6fcc2ff0db7a4f490f5e0ce9e24691f3": "Relacja {{HasOne}} nie może tworzyć więcej niż jednej instancji elementu {0}",
|
||||
"728232e473bf80272c042df2b7e002f4": "Relacja {{polymorphic}} {0}: {1} wymaga parametru `polymorphic.discriminator`, jeśli określono parametr `polymorphic.foreignKey`",
|
||||
"791ab3031a73ede03f7d6299a85e8289": "Przekroczono limit czasu połączenia po {0} ms",
|
||||
"7b277018e43d41bc445731092b91547d": "Nie połączono",
|
||||
"7bbbdece4eea90e42aa5c0bce295e503": "{{Model::findById}} wymaga argumentu {{id}}",
|
||||
"7e9530c0399289be0ee601a604be71ff": "Relacja {{BelongsTo}} {0} jest pusta",
|
||||
"7faa840eb6ce11250a141deb42a6c489": "Nieznana relacja {{scope}}: {0}",
|
||||
"8091838319a5cc7d6a34af2f2a616ce9": "Nazwą właściwości nie może być \"{{constructor}}\" w modelu: {0}",
|
||||
"881e4b0cb86ed59549248ee540a9fd10": "Nazwa właściwości \"{{constructor}}\" nie jest dozwolona w danych {0}",
|
||||
"89afd3a9249f5a8d3edda07d84ca049d": "Model {{Polymorphic}} nie został znaleziony: `{0}`",
|
||||
"89bf6d92731fe7bd2146ce8d0bec205c": "Niepoprawny argument; musi to być łańcuch, literał {{regex}} lub obiekt {{RegExp}}",
|
||||
"8a39126103a157f501affa070367a1b0": "Instancja {0} nie jest poprawna. Szczegóły: {1}.",
|
||||
"8c5ab01638c1ac1d58168c6346a8481a": "Niepoprawne flagi {{regex}}: {0}",
|
||||
"938401ea4ce48159efa9be1d4a5e8bab": "Elementy muszą być tablicą: {0}",
|
||||
"9e1f143ee02946324d34da92f71bf74e": "Relacja {0}: {1} wymaga parametru `model`",
|
||||
"a004f310d315e592843776fab964eaeb": "Relacje {{Polymorphic}} wymagają modelu pośredniego",
|
||||
"a0cf0e09c26df14283223e84e6a10f00": "Nie można zaktualizować atrybutów. Obiekt {{Object}} o identyfikatorze {{id}} {0} nie istnieje!",
|
||||
"a2487abefef4259c2131d96cdb8543b1": "Nawiązanie połączenia nie powiodło się: {0}\nZostanie podjęta ponowna próba wykonania następnego żądania.",
|
||||
"a25e41a39c60c4702e55d0c3936576a1": "Niezgodność klucza: {0}.{1}: {2}, {3}.{4}: {5}",
|
||||
"a327355560d495454fba2c1aad6bdf09": "Nieznana metoda zasięgu: {0}",
|
||||
"a6c18a7f4390cd3d59a2a7a047ae2aab": "Uruchom komendę \"{{npm install loopback-datasource-juggler}} {0}\"",
|
||||
"a829dee089c912e68c18920ba015400c": "OSTRZEŻENIE: właściwość {{id}} nie może zostać zmieniona z {0} na {1} dla modelu: {2} w haku operacji {{'loaded'}}",
|
||||
"a984a076c59e451948b2bcf7a393d860": "OSTRZEŻENIE: właściwość {{id}} nie może zostać zmieniona z {0} na {1} dla modelu: {2} w haku operacji {{'before save'}}",
|
||||
"ac04cf275b71c1eb89a41cf6bbad7a64": "Metoda HasOne \"getAsync()\" jest nieaktualna, zamiast niej użyj metody \"get()\".",
|
||||
"b138294f132edfe1eb2a8211150c7238": "Nieoczekiwany element `undefined` w zapytaniu",
|
||||
"b15b20280211ad258d92947f05b6e4a5": "Konektor nie został zainicjowany.",
|
||||
"b278876ec93ef9760f00e83f38ba313d": "Metoda Scope \"getAsync()\" jest nieaktualna, zamiast niej użyj metody \"find()\".",
|
||||
"ba0fd8106eb54de4d003a844206431fd": "Hak modelu \"{0}\" jest nieaktualny, zamiast niego użyj haków operacji. {{http://docs.strongloop.com/display/LB/Operation+hooks}}",
|
||||
"baf2c8b0c5a574b8a894e9b6304fece1": "Klauzula where {0} nie jest obiektem {{object}}",
|
||||
"bdb11cc1c780c9ccac33c316cfdc9d82": "Nie zdefiniowano typu dla właściwości {0}.{1}",
|
||||
"bdfb951c8ff7ce0cbc08c06f548fd927": "Wartość jest pustym elementem {{object}}",
|
||||
"bec226891a505828bfc76c5cfd73b336": "Nie można uzyskać wartości TTL dla nieznanego klucza {0}",
|
||||
"cd930369e86cdd222f7bd117c6f9fa94": "Nieznany dostawca wartości domyślnych {0}",
|
||||
"cfee4d8149316d9a647c0885cf3cafaf": "Nazwy właściwości zawierające kropki nie są obsługiwane. Model: {0}, właściwość dynamiczna: {1}",
|
||||
"d40328eabd8756d795bcdd49d782d4e9": "Źródło danych nie obsługuje transakcji",
|
||||
"da02dd6c53d4148320eeb31718a7aebe": "Niepoprawny typ właściwości {0}",
|
||||
"da751a8a748adbde5b55fa83b707b4e2": "Nazwy właściwości zawierające kropki nie są obsługiwane. Model: {0}, właściwość: {1}",
|
||||
"db03083e9a768388fdbee865249ac67a": "Ignorowanie błędów sprawdzania poprawności w metodzie {{updateOrCreate()}}:",
|
||||
"dd63416d9b7d9fa4181e89efd619dfd8": "Wartość nie jestem elementem {{array}} lub {{object}} z sekwencyjnymi indeksami liczbowymi",
|
||||
"ddf0aa14803f1c84f4a97f3803f7471c": "Nazwa klasy jest wymagana",
|
||||
"e08ab0e1ab55f26c357061447b635905": "Nie znaleziono relacji w {0} dla ({1}.{2}.{3}.{4})",
|
||||
"e0e9504e137a3c3339144b51ed76fef2": "Konektor nie został poprawnie zdefiniowany: powinien utworzyć element '{{connector}}' źródła danych",
|
||||
"e2f282cbe3efba001d6d3a09f7f6ca8c": "Relacja {{polymorphic}} {0}: {1} wymaga parametru `polymorphic.foreignKey`, jeśli określono parametr `polymorphic.discriminator`",
|
||||
"e39e0f5d52bfbf511e645d19ecadd2fa": "Właściwość {0} ma niepoprawną klauzulę {1}: {2}",
|
||||
"e4434de4bb8f5a3cd1d416e4d80d7e0b": "Nieznany identyfikator {{id}} \"{0}\" \"{1}\".",
|
||||
"e54d944c2a2c85a23caa86027ae307cf": "Nie można migrować modeli, które nie są przyłączone do tego źródła danych: {0}",
|
||||
"e54f118a75e15e132f16b985274eb46d": "Filtr zapytania {0} nie jest obiektem {{object}}",
|
||||
"e55937649d8d7a11706b8cec22d02eae": "Relacja {{HasOne}} {0} jest pusta",
|
||||
"e6161ae8459c79d810e2aa9d21282a39": "Podczas aktualizowania atrybutów należy podać identyfikator {{id}}!",
|
||||
"eb56c2b0c30cf006e2df00a549ec9c2c": "Relacja \"{0}\" nie została zdefiniowana dla modelu {1}",
|
||||
"ec42dca074f1818c447f7ad16e2d01af": "Element {0} nie został udostępniony przez przyłączony konektor",
|
||||
"ecb7aa804bf54c682999d20d6436104c": "Transakcja {{transaction}} nie jest aktywna: {0}",
|
||||
"f30809cb932b72a66416a709c8531530": "Metoda {{method}} w transakcji nie jest obsługiwana przez konektor",
|
||||
"f41bd91dc0f000a79c0bf842f1b7fdf9": "nie można utworzyć listy z łańcucha JSON: {0}",
|
||||
"f6e8c96c93b9c7687d6c172b3695e898": "Właściwość {{id}} ({0}) nie może zostać zaktualizowana z {1} na {2}",
|
||||
"fa9ae17e8e008d0eb0f0421a2972308c": "Relacja {{polymorphic}} {0}: {1} wymaga parametru `model`",
|
||||
"fca4d12faff1035d9d0438d73432571b": "Zduplikowany wpis dla {0}.{1}",
|
||||
"fd3cc89dc67e2d604eaae21bdf41d403": "Nie można znaleźć relacji {0} dla modelu {1}",
|
||||
"fec8ebda24db46a9d040bf863765cc44": "Operator {0} zawiera niepoprawne klauzule {1}: {2}"
|
||||
}
|
||||
|
|
@ -0,0 +1,97 @@
|
|||
{
|
||||
"0483a77cf77741504204e5c066597487": "Связь {0} {{polymorphic}}: {1} не предполагает параметр `polymorphic.as` при определении пользовательского атрибута `foreignKey`/`discriminator` ",
|
||||
"09483e03b91c8bd58732a74b3ef1ec13": "Недопустимая дата: {0}",
|
||||
"0a5aa17f7866a85e3aee37ef5369403c": "LinkManyToMany полученный целевой объект не содержит обязательный параметр \"{0}\"",
|
||||
"0b16d3ffc42f91b4b9a4b3b50c41c838": "Недопустимый порядок {0}",
|
||||
"0bd753a8944ad0af85a939bb25273887": "Не удалось преобразовать неизвестный ключ {0} в устаревший",
|
||||
"0c0b867aca0973ba26e887d3337cc4ec": "Модель {{Polymorphic}} не найдена: не задан параметр `{0}`",
|
||||
"0c4eb8b6c2ff6e51d7e195eee346ced9": "Таблица '{0}' не существует.",
|
||||
"0ff31abb394afb555df162e74ff1a0a0": "Для {{id}} не удается изменить значение {0} на {1}, если параметру {{forceId}} присвоено значение true",
|
||||
"1ae7d3e0be381efb32bfd1ba652f5172": "ПРЕДУПРЕЖДЕНИЕ: связь {0} {{polymorphic}}: {1} использует ключевое слово `polymorphic.as`, которое УСТАРЕЕТ в LoopBack.next. Решение по замене приведены в следующем документе: (https://loopback.io/doc/en/lb3/Polymorphic-relations.html#deprecated-polymorphic-as)",
|
||||
"1daef4e937fe52136597ba8fd2060f55": "Вложенные транзакции не поддерживаются",
|
||||
"21095484501dbff31af6556fa6039182": "Параметр {{offset/skip}} {0} недопустим",
|
||||
"280f4550f90e133118955ec6f6f72830": "Указан тип дискриминатора {0}, но модель с таким именем не существует",
|
||||
"28697ec15968a7969211f6d035ba9260": "Связь {0} {{polymorphic}}: в {1} не используется параметр `model`",
|
||||
"2c4904377a87fdab502118719cc0d266": "{{Transaction}} не поддерживается",
|
||||
"2c5c8519721f749aab13c2f04f41d611": "Свойство {0} содержит недопустимый оператор {1}: требуется ровно 2 значения, но получено {2}",
|
||||
"2f4af31c144bbfab1bbf479866acd820": "\nПРЕДУПРЕЖДЕНИЕ: Коннектор {{LoopBack}} \"{0}\" не установлен как один из следующих модулей:\n\n {1}\n\nДля исправления этой ошибки выполните следующую команду:\n\n {{npm install {2} --save}}\n",
|
||||
"3864f9be10f27723074566d2b3893514": "Предупреждение: Модель {0} {{strict mode: `throw`}} была удалена, используйте вместо нее {{`strict: true`}}, возвращающий для неизвестных свойств значение {{`Validation Error`}},",
|
||||
"38dbf42c29a4645238cc3d632e88ebc9": "Параметр {{Relation.modelTo}} не определен для связи {0} и не является {{polymorphic}}",
|
||||
"3cde8cc9bca22c67278b202ab0720106": "Не найден экземпляр с ИД {0} для {1}",
|
||||
"416dfbb7b823f51c9f3800be81060b41": "Не найден экземпляр с {{id}} {0} для {1}",
|
||||
"49b5afd8c6a19ad9c8abeffb2f8114eb": "Метод BelongsTo \"getAsync()\" устарел, используйте вместо него \"get()\".",
|
||||
"4c78325cedbb826db3a05bf5df0e8546": "При замене необходимо указать {{id}}!",
|
||||
"4e31b1edd10dadb724d83387de0b5062": "Параметр {{limit}} {0} недопустим",
|
||||
"514985b2327f061ffb1c932f6b909979": "Модель {0} не определена.",
|
||||
"525c856e65daab43be247e7b5410febd": "Связь {0} {{polymorphic}}: {1} не предполагает параметр `polymorphic.selector` при определении пользовательского атрибута `foreignKey`/`discriminator` ",
|
||||
"5c18ee111dd87540cdb19a2a93b33be9": "Выполнен откат транзакции из-за тайм-аута",
|
||||
"5ec7e6664256f7ea78f4f06dafc7d974": "Транзакция не готова, дождитесь возвращенного обязательства устранить неполадку",
|
||||
"5ec8efeb715a2c34b440f2d76e2cf87d": " {0}",
|
||||
"6111399276924ffa3bc9a410cdfcb2e5": "Отсутствует имя {{id}} {0}",
|
||||
"614e3355647e4127c96256102dc63376": "Свойство {0} содержит недопустимый оператор {1}: ожидается строка или регулярное выражение",
|
||||
"62a2d80c405b7fec5f547c448ab1b6ff": "Недопустимое направление {{order}} {0}",
|
||||
"6502a117987610380b9068ef98b1b0ee": "В {0} не обнаружены записи для ({1}.{2} ,{3}.{4})",
|
||||
"67c2bf43b5281ab929617423ea8a6f3e": "Коннектор {0} не поддерживает операцию {{replaceById}}. Это не является ошибкой LoopBack. Обратитесь к авторам коннектора, желательно через раздел ошибок на GitHub.",
|
||||
"6c3234937d69763fc7f6bcafccc59bbc": "Для {{Model::deleteById}} требуется аргумент {{id}}",
|
||||
"6eb6fd4fbd73394000bc25f5776fd20c": "Для {{Model::exists}} требуется аргумент {{id}}",
|
||||
"6fcc2ff0db7a4f490f5e0ce9e24691f3": "Связи {{HasOne}} не удается создать больше одного экземпляра {0}",
|
||||
"728232e473bf80272c042df2b7e002f4": "Связь {0} {{polymorphic}}: для {1} требуется параметр `polymorphic.discriminator`, если задан параметр `polymorphic.foreignKey`",
|
||||
"791ab3031a73ede03f7d6299a85e8289": "Тайм-аут соединения наступает через {0} мс",
|
||||
"7b277018e43d41bc445731092b91547d": "Не подключено",
|
||||
"7bbbdece4eea90e42aa5c0bce295e503": "Для {{Model::findById}} требуется аргумент {{id}}",
|
||||
"7e9530c0399289be0ee601a604be71ff": "Пустая связь {{BelongsTo}} {0}",
|
||||
"7faa840eb6ce11250a141deb42a6c489": "Неизвестная связь {{scope}}: {0}",
|
||||
"8091838319a5cc7d6a34af2f2a616ce9": "Имя свойства не должно быть \"{{constructor}}\" в модели: {0}",
|
||||
"881e4b0cb86ed59549248ee540a9fd10": "Имя свойства \"{{constructor}}\" не разрешено в данных {0}",
|
||||
"89afd3a9249f5a8d3edda07d84ca049d": "Модель {{Polymorphic}} не найдена: `{0}`",
|
||||
"89bf6d92731fe7bd2146ce8d0bec205c": "Недопустимый аргумент, требуется строка, литерал {{regex}} или объект {{RegExp}}",
|
||||
"8a39126103a157f501affa070367a1b0": "Недопустимый экземпляр {0}. Сведения: {1}.",
|
||||
"8c5ab01638c1ac1d58168c6346a8481a": "Недопустимые флаги {{regex}}: {0}",
|
||||
"938401ea4ce48159efa9be1d4a5e8bab": "Элементы должны быть массивом: {0}",
|
||||
"9e1f143ee02946324d34da92f71bf74e": "Связь {0}: для {1} требуется параметр `model`",
|
||||
"a004f310d315e592843776fab964eaeb": "Связи {{Polymorphic}} требуется промежуточная модель",
|
||||
"a0cf0e09c26df14283223e84e6a10f00": "Не удалось обновить атрибуты. {{Object}} с {{id}} {0} не существует",
|
||||
"a2487abefef4259c2131d96cdb8543b1": "Соединение не выполнено: {0}\nПопытка соединения будет выполнена повторно при следующем запросе.",
|
||||
"a25e41a39c60c4702e55d0c3936576a1": "Несоответствие ключей: {0}.{1}: {2}, {3}.{4}: {5}",
|
||||
"a327355560d495454fba2c1aad6bdf09": "Неизвестный метод области: {0}",
|
||||
"a6c18a7f4390cd3d59a2a7a047ae2aab": "Выполните команду \"{{npm install loopback-datasource-juggler}} {0}\" ",
|
||||
"a829dee089c912e68c18920ba015400c": "Предупреждение: значение свойства {{id}} {0} не может быть изменено на {1} для модели {2} в перехватчике операции {{'loaded'}}",
|
||||
"a984a076c59e451948b2bcf7a393d860": "Предупреждение: значение свойства {{id}} {0} не может быть изменено на {1} для модели {2} в перехватчике операции {{'before save'}}",
|
||||
"ac04cf275b71c1eb89a41cf6bbad7a64": "Метод HasOne \"getAsync()\" устарел, используйте вместо него \"get()\".",
|
||||
"b138294f132edfe1eb2a8211150c7238": "Непредвиденный параметр `undefined` в запросе",
|
||||
"b15b20280211ad258d92947f05b6e4a5": "Коннектор не инициализирован.",
|
||||
"b278876ec93ef9760f00e83f38ba313d": "Метод Scope \"getAsync()\" устарел, используйте вместо него \"find()\".",
|
||||
"ba0fd8106eb54de4d003a844206431fd": "Перехватчик Model \"{0}\" устарел, используйте вместо него перехватчики Operation. {{http://docs.strongloop.com/display/LB/Operation+hooks}}",
|
||||
"baf2c8b0c5a574b8a894e9b6304fece1": "Оператор where {0} не является объектом {{object}}",
|
||||
"bdb11cc1c780c9ccac33c316cfdc9d82": "Не определен тип для свойства {0}.{1}",
|
||||
"bdfb951c8ff7ce0cbc08c06f548fd927": "Значение - пустой объект {{object}}",
|
||||
"bec226891a505828bfc76c5cfd73b336": "Не удалось получить TTL для неизвестного ключа {0}",
|
||||
"cd930369e86cdd222f7bd117c6f9fa94": "Неизвестный поставщик значений по умолчанию {0}",
|
||||
"cfee4d8149316d9a647c0885cf3cafaf": "Не поддерживаются имена свойств, содержащие точку. Модель: {0}, динамическое свойство: {1}",
|
||||
"d40328eabd8756d795bcdd49d782d4e9": "DataSource не поддерживает транзакции",
|
||||
"da02dd6c53d4148320eeb31718a7aebe": "Недопустимый тип для свойства {0}",
|
||||
"da751a8a748adbde5b55fa83b707b4e2": "Не поддерживаются имена свойств, содержащие точку. Модель: {0}, свойство: {1}",
|
||||
"db03083e9a768388fdbee865249ac67a": "Ошибки проверки в {{updateOrCreate()}} игнорируются:",
|
||||
"dd63416d9b7d9fa4181e89efd619dfd8": "Значение не является {{array}} или {{object}} с последовательными числовыми индексами",
|
||||
"ddf0aa14803f1c84f4a97f3803f7471c": "Необходимо указать имя класса",
|
||||
"e08ab0e1ab55f26c357061447b635905": "В {0} не найдена связь для ({1}.{2},{3}.{4})",
|
||||
"e0e9504e137a3c3339144b51ed76fef2": "Неправильно определен коннектор: он должен создавать элемент `{{connector}}` в dataSource",
|
||||
"e2f282cbe3efba001d6d3a09f7f6ca8c": "Связь {0} {{polymorphic}}: для {1} требуется параметр `polymorphic.foreignKey`, если указан параметр `polymorphic.discriminator`",
|
||||
"e39e0f5d52bfbf511e645d19ecadd2fa": "Свойство {0} содержит недопустимый оператор {1}: {2}",
|
||||
"e4434de4bb8f5a3cd1d416e4d80d7e0b": "Неизвестный {{id}} \"{0}\" \"{1}\".",
|
||||
"e54d944c2a2c85a23caa86027ae307cf": "Не удается перенести модели, которые не подключены к этому источнику данных: {0}",
|
||||
"e54f118a75e15e132f16b985274eb46d": "Фильтр запроса {0} не является объектом {{object}}",
|
||||
"e55937649d8d7a11706b8cec22d02eae": "Пустая связь {{HasOne}} {0}",
|
||||
"e6161ae8459c79d810e2aa9d21282a39": "При обновлении атрибутов необходимо указать {{id}}",
|
||||
"eb56c2b0c30cf006e2df00a549ec9c2c": "Связь \"{0}\" не определена для модели {1}",
|
||||
"ec42dca074f1818c447f7ad16e2d01af": "Подключенный коннектор не указал {0}",
|
||||
"ecb7aa804bf54c682999d20d6436104c": "Неактивная транзакция {{transaction}}: {0}",
|
||||
"f30809cb932b72a66416a709c8531530": "Коннектор не поддерживает {{method}} в транзакции",
|
||||
"f41bd91dc0f000a79c0bf842f1b7fdf9": "Не удалось создать атрибут List из строки JSON: {0}",
|
||||
"f6e8c96c93b9c7687d6c172b3695e898": "Для свойства {{id}} ({0}) не удалось изменить значение {1} на {2}",
|
||||
"fa9ae17e8e008d0eb0f0421a2972308c": "Связь {0} {{polymorphic}}: для {1} требуется параметр `model`",
|
||||
"fca4d12faff1035d9d0438d73432571b": "Создать копию для {0}.{1}",
|
||||
"fd3cc89dc67e2d604eaae21bdf41d403": "Не удалось найти связь {0} для модели {1}",
|
||||
"fec8ebda24db46a9d040bf863765cc44": "Оператор {0} содержит недопустимые операторы {1}: {2}"
|
||||
}
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
// Copyright IBM Corp. 2015,2016. All Rights Reserved.
|
||||
// Copyright IBM Corp. 2015,2019. All Rights Reserved.
|
||||
// Node module: loopback-datasource-juggler
|
||||
// This file is licensed under the MIT License.
|
||||
// License text available at https://opensource.org/licenses/MIT
|
||||
|
@ -7,7 +7,7 @@
|
|||
|
||||
// A lightweight alternative to "depd" that works in the browser
|
||||
module.exports = function depd(namespace) {
|
||||
var warned = {};
|
||||
const warned = {};
|
||||
return function deprecate(message) {
|
||||
if (warned[message]) return;
|
||||
warned[message] = true;
|
||||
|
|
|
@ -1,15 +1,20 @@
|
|||
// Copyright IBM Corp. 2016,2019. All Rights Reserved.
|
||||
// Node module: loopback-datasource-juggler
|
||||
// This file is licensed under the MIT License.
|
||||
// License text available at https://opensource.org/licenses/MIT
|
||||
|
||||
'use strict';
|
||||
|
||||
var g = require('strong-globalize')();
|
||||
const g = require('strong-globalize')();
|
||||
|
||||
var assert = require('assert');
|
||||
var Connector = require('loopback-connector').Connector;
|
||||
var debug = require('debug')('loopback:connector:kv-memory');
|
||||
var minimatch = require('minimatch');
|
||||
var util = require('util');
|
||||
const assert = require('assert');
|
||||
const Connector = require('loopback-connector').Connector;
|
||||
const debug = require('debug')('loopback:connector:kv-memory');
|
||||
const minimatch = require('minimatch');
|
||||
const util = require('util');
|
||||
|
||||
exports.initialize = function initializeDataSource(dataSource, cb) {
|
||||
var settings = dataSource.settings;
|
||||
const settings = dataSource.settings;
|
||||
dataSource.connector = new KeyValueMemoryConnector(settings, dataSource);
|
||||
if (cb) process.nextTick(cb);
|
||||
};
|
||||
|
@ -25,7 +30,7 @@ function KeyValueMemoryConnector(settings, dataSource) {
|
|||
this._store = Object.create(null);
|
||||
|
||||
this._setupRegularCleanup();
|
||||
};
|
||||
}
|
||||
util.inherits(KeyValueMemoryConnector, Connector);
|
||||
|
||||
KeyValueMemoryConnector.prototype._setupRegularCleanup = function() {
|
||||
|
@ -33,25 +38,25 @@ KeyValueMemoryConnector.prototype._setupRegularCleanup = function() {
|
|||
// in order to release memory. Note that GET operation checks
|
||||
// key expiration too, the scheduled cleanup is merely a performance
|
||||
// optimization.
|
||||
var self = this;
|
||||
var timer = this._cleanupTimer = setInterval(
|
||||
function() {
|
||||
if (self && self._removeExpiredItems) {
|
||||
self._removeExpiredItems();
|
||||
this._cleanupTimer = setInterval(
|
||||
() => {
|
||||
if (this && this._removeExpiredItems) {
|
||||
this._removeExpiredItems();
|
||||
} else {
|
||||
// The datasource/connector was destroyed - cancel the timer
|
||||
clearInterval(timer);
|
||||
clearInterval(this._cleanupTimer);
|
||||
}
|
||||
},
|
||||
1000);
|
||||
1000,
|
||||
);
|
||||
this._cleanupTimer.unref();
|
||||
};
|
||||
|
||||
KeyValueMemoryConnector._removeExpiredItems = function() {
|
||||
debug('Running scheduled cleanup of expired items.');
|
||||
for (var modelName in this._store) {
|
||||
var modelStore = this._store[modelName];
|
||||
for (var key in modelStore) {
|
||||
for (const modelName in this._store) {
|
||||
const modelStore = this._store[modelName];
|
||||
for (const key in modelStore) {
|
||||
if (modelStore[key].isExpired()) {
|
||||
debug('Removing expired key', key);
|
||||
delete modelStore[key];
|
||||
|
@ -68,8 +73,8 @@ KeyValueMemoryConnector.prototype._getStoreForModel = function(modelName) {
|
|||
};
|
||||
|
||||
KeyValueMemoryConnector.prototype._removeIfExpired = function(modelName, key) {
|
||||
var store = this._getStoreForModel(modelName);
|
||||
var item = store[key];
|
||||
const store = this._getStoreForModel(modelName);
|
||||
let item = store[key];
|
||||
if (item && item.isExpired()) {
|
||||
debug('Removing expired key', key);
|
||||
delete store[key];
|
||||
|
@ -83,9 +88,9 @@ KeyValueMemoryConnector.prototype.get =
|
|||
function(modelName, key, options, callback) {
|
||||
this._removeIfExpired(modelName, key);
|
||||
|
||||
var store = this._getStoreForModel(modelName);
|
||||
var item = store[key];
|
||||
var value = item ? item.value : null;
|
||||
const store = this._getStoreForModel(modelName);
|
||||
const item = store[key];
|
||||
let value = item ? item.value : null;
|
||||
debug('GET %j %j -> %s', modelName, key, value);
|
||||
|
||||
if (/^buffer:/.test(value)) {
|
||||
|
@ -103,7 +108,7 @@ function(modelName, key, options, callback) {
|
|||
|
||||
KeyValueMemoryConnector.prototype.set =
|
||||
function(modelName, key, value, options, callback) {
|
||||
var store = this._getStoreForModel(modelName);
|
||||
const store = this._getStoreForModel(modelName);
|
||||
if (Buffer.isBuffer(value)) {
|
||||
value = 'buffer:' + value.toString('base64');
|
||||
} else if (value instanceof Date) {
|
||||
|
@ -122,11 +127,11 @@ KeyValueMemoryConnector.prototype.expire =
|
|||
function(modelName, key, ttl, options, callback) {
|
||||
this._removeIfExpired(modelName, key);
|
||||
|
||||
var store = this._getStoreForModel(modelName);
|
||||
const store = this._getStoreForModel(modelName);
|
||||
|
||||
if (!(key in store)) {
|
||||
return process.nextTick(function() {
|
||||
var err = new Error(g.f('Cannot expire unknown key %j', key));
|
||||
const err = new Error(g.f('Cannot expire unknown key %j', key));
|
||||
err.statusCode = 404;
|
||||
callback(err);
|
||||
});
|
||||
|
@ -141,18 +146,18 @@ KeyValueMemoryConnector.prototype.ttl =
|
|||
function(modelName, key, options, callback) {
|
||||
this._removeIfExpired(modelName, key);
|
||||
|
||||
var store = this._getStoreForModel(modelName);
|
||||
const store = this._getStoreForModel(modelName);
|
||||
|
||||
// key is unknown
|
||||
if (!(key in store)) {
|
||||
return process.nextTick(function() {
|
||||
var err = new Error(g.f('Cannot get TTL for unknown key %j', key));
|
||||
const err = new Error(g.f('Cannot get TTL for unknown key %j', key));
|
||||
err.statusCode = 404;
|
||||
callback(err);
|
||||
});
|
||||
}
|
||||
|
||||
var ttl = store[key].getTtl();
|
||||
const ttl = store[key].getTtl();
|
||||
debug('TTL %j %j -> %s', modelName, key, ttl);
|
||||
|
||||
process.nextTick(function() {
|
||||
|
@ -162,20 +167,20 @@ function(modelName, key, options, callback) {
|
|||
|
||||
KeyValueMemoryConnector.prototype.iterateKeys =
|
||||
function(modelName, filter, options, callback) {
|
||||
var store = this._getStoreForModel(modelName);
|
||||
var self = this;
|
||||
var checkFilter = createMatcher(filter.match);
|
||||
const store = this._getStoreForModel(modelName);
|
||||
const self = this;
|
||||
const checkFilter = createMatcher(filter.match);
|
||||
|
||||
var keys = Object.keys(store).filter(function(key) {
|
||||
const keys = Object.keys(store).filter(function(key) {
|
||||
return !self._removeIfExpired(modelName, key) && checkFilter(key);
|
||||
});
|
||||
|
||||
debug('ITERATE KEYS %j -> %s keys', modelName, keys.length);
|
||||
|
||||
var ix = 0;
|
||||
let ix = 0;
|
||||
return {
|
||||
next: function(cb) {
|
||||
var value = ix < keys.length ? keys[ix++] : undefined;
|
||||
const value = ix < keys.length ? keys[ix++] : undefined;
|
||||
setImmediate(function() { cb(null, value); });
|
||||
},
|
||||
};
|
||||
|
@ -202,15 +207,15 @@ KeyValueMemoryConnector.prototype.disconnect = function(callback) {
|
|||
|
||||
KeyValueMemoryConnector.prototype.delete =
|
||||
function(modelName, key, options, callback) {
|
||||
var store = this._getStoreForModel(modelName);
|
||||
const store = this._getStoreForModel(modelName);
|
||||
delete store[key];
|
||||
callback();
|
||||
};
|
||||
|
||||
KeyValueMemoryConnector.prototype.deleteAll =
|
||||
function(modelName, options, callback) {
|
||||
var modelStore = this._getStoreForModel(modelName);
|
||||
for (var key in modelStore)
|
||||
const modelStore = this._getStoreForModel(modelName);
|
||||
for (const key in modelStore)
|
||||
delete modelStore[key];
|
||||
callback();
|
||||
};
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// Copyright IBM Corp. 2013,2016. All Rights Reserved.
|
||||
// Copyright IBM Corp. 2013,2019. All Rights Reserved.
|
||||
// Node module: loopback-datasource-juggler
|
||||
// This file is licensed under the MIT License.
|
||||
// License text available at https://opensource.org/licenses/MIT
|
||||
|
@ -6,14 +6,14 @@
|
|||
'use strict';
|
||||
|
||||
/* global window:false */
|
||||
var g = require('strong-globalize')();
|
||||
var util = require('util');
|
||||
var Connector = require('loopback-connector').Connector;
|
||||
var geo = require('../geo');
|
||||
var utils = require('../utils');
|
||||
var fs = require('fs');
|
||||
var async = require('async');
|
||||
var debug = require('debug')('loopback:connector:memory');
|
||||
const g = require('strong-globalize')();
|
||||
const util = require('util');
|
||||
const Connector = require('loopback-connector').Connector;
|
||||
const geo = require('../geo');
|
||||
const utils = require('../utils');
|
||||
const fs = require('fs');
|
||||
const async = require('async');
|
||||
const debug = require('debug')('loopback:connector:memory');
|
||||
|
||||
/**
|
||||
* Initialize the Memory connector against the given data source
|
||||
|
@ -47,6 +47,8 @@ function Memory(m, settings) {
|
|||
|
||||
util.inherits(Memory, Connector);
|
||||
|
||||
Memory.prototype.multiInsertSupported = true;
|
||||
|
||||
Memory.prototype.getDefaultIdType = function() {
|
||||
return Number;
|
||||
};
|
||||
|
@ -82,7 +84,7 @@ function deserialize(dbObj) {
|
|||
}
|
||||
|
||||
Memory.prototype.getCollection = function(model) {
|
||||
var modelClass = this._models[model];
|
||||
const modelClass = this._models[model];
|
||||
if (modelClass && modelClass.settings.memory) {
|
||||
model = modelClass.settings.memory.collection || model;
|
||||
}
|
||||
|
@ -111,15 +113,15 @@ Memory.prototype.collectionSeq = function(model, val) {
|
|||
* @returns {*} The file operation queue
|
||||
*/
|
||||
Memory.prototype.setupFileQueue = function() {
|
||||
var self = this;
|
||||
const self = this;
|
||||
if (!this.fileQueue) {
|
||||
// Create a queue for writes
|
||||
this.fileQueue = async.queue(function(task, done) {
|
||||
var callback = task.callback || function() {};
|
||||
var file = self.settings.file;
|
||||
const callback = task.callback || function() {};
|
||||
const file = self.settings.file;
|
||||
if (task.operation === 'write') {
|
||||
// Flush out the models/ids
|
||||
var data = JSON.stringify({
|
||||
const data = JSON.stringify({
|
||||
ids: self.ids,
|
||||
models: self.cache,
|
||||
}, null, ' ');
|
||||
|
@ -130,7 +132,7 @@ Memory.prototype.setupFileQueue = function() {
|
|||
callback(err, task.data);
|
||||
});
|
||||
} else if (task.operation === 'read') {
|
||||
debug('Reading cache from %s: %s', file, data);
|
||||
debug('Reading cache from %s', file);
|
||||
fs.readFile(file, {
|
||||
encoding: 'utf8',
|
||||
flag: 'r',
|
||||
|
@ -147,7 +149,7 @@ Memory.prototype.setupFileQueue = function() {
|
|||
}
|
||||
});
|
||||
} else {
|
||||
var err = new Error('Unknown type of task');
|
||||
const err = new Error('Unknown type of task');
|
||||
done(err);
|
||||
callback(err);
|
||||
}
|
||||
|
@ -176,8 +178,8 @@ Memory.prototype.parseAndLoad = function(data, callback) {
|
|||
};
|
||||
|
||||
Memory.prototype.loadFromFile = function(callback) {
|
||||
var hasLocalStorage = typeof window !== 'undefined' && window.localStorage;
|
||||
var localStorage = hasLocalStorage && this.settings.localStorage;
|
||||
const hasLocalStorage = typeof window !== 'undefined' && window.localStorage;
|
||||
const localStorage = hasLocalStorage && this.settings.localStorage;
|
||||
|
||||
if (this.settings.file) {
|
||||
debug('Queueing read %s', this.settings.file);
|
||||
|
@ -186,7 +188,7 @@ Memory.prototype.loadFromFile = function(callback) {
|
|||
callback: callback,
|
||||
});
|
||||
} else if (localStorage) {
|
||||
var data = window.localStorage.getItem(localStorage);
|
||||
let data = window.localStorage.getItem(localStorage);
|
||||
data = data || '{}';
|
||||
this.parseAndLoad(data, callback);
|
||||
} else {
|
||||
|
@ -199,9 +201,9 @@ Memory.prototype.loadFromFile = function(callback) {
|
|||
* @param {Function} callback
|
||||
*/
|
||||
Memory.prototype.saveToFile = function(result, callback) {
|
||||
var file = this.settings.file;
|
||||
var hasLocalStorage = typeof window !== 'undefined' && window.localStorage;
|
||||
var localStorage = hasLocalStorage && this.settings.localStorage;
|
||||
const file = this.settings.file;
|
||||
const hasLocalStorage = typeof window !== 'undefined' && window.localStorage;
|
||||
const localStorage = hasLocalStorage && this.settings.localStorage;
|
||||
if (file) {
|
||||
debug('Queueing write %s', this.settings.file);
|
||||
// Enqueue the write
|
||||
|
@ -212,7 +214,7 @@ Memory.prototype.saveToFile = function(result, callback) {
|
|||
});
|
||||
} else if (localStorage) {
|
||||
// Flush out the models/ids
|
||||
var data = JSON.stringify({
|
||||
const data = JSON.stringify({
|
||||
ids: this.ids,
|
||||
models: this.cache,
|
||||
}, null, ' ');
|
||||
|
@ -229,26 +231,26 @@ Memory.prototype.saveToFile = function(result, callback) {
|
|||
|
||||
Memory.prototype.define = function defineModel(definition) {
|
||||
this.constructor.super_.prototype.define.apply(this, [].slice.call(arguments));
|
||||
var m = definition.model.modelName;
|
||||
const m = definition.model.modelName;
|
||||
if (!this.collection(m)) this.initCollection(m);
|
||||
};
|
||||
|
||||
Memory.prototype._createSync = function(model, data, fn) {
|
||||
// FIXME: [rfeng] We need to generate unique ids based on the id type
|
||||
// FIXME: [rfeng] We don't support composite ids yet
|
||||
var currentId = this.collectionSeq(model);
|
||||
let currentId = this.collectionSeq(model);
|
||||
if (currentId === undefined) { // First time
|
||||
currentId = this.collectionSeq(model, 1);
|
||||
}
|
||||
var id = this.getIdValue(model, data) || currentId;
|
||||
let id = this.getIdValue(model, data) || currentId;
|
||||
if (id > currentId) {
|
||||
// If the id is passed in and the value is greater than the current id
|
||||
currentId = id;
|
||||
}
|
||||
this.collectionSeq(model, Number(currentId) + 1);
|
||||
|
||||
var props = this._models[model].properties;
|
||||
var idName = this.idName(model);
|
||||
const props = this._models[model].properties;
|
||||
const idName = this.idName(model);
|
||||
id = (props[idName] && props[idName].type && props[idName].type(id)) || id;
|
||||
this.setIdValue(model, data, id);
|
||||
if (!this.collection(model)) {
|
||||
|
@ -256,7 +258,7 @@ Memory.prototype._createSync = function(model, data, fn) {
|
|||
}
|
||||
|
||||
if (this.collection(model)[id]) {
|
||||
var error = new Error(g.f('Duplicate entry for %s.%s', model, idName));
|
||||
const error = new Error(g.f('Duplicate entry for %s.%s', model, idName));
|
||||
error.statusCode = error.status = 409;
|
||||
return fn(error);
|
||||
}
|
||||
|
@ -266,19 +268,42 @@ Memory.prototype._createSync = function(model, data, fn) {
|
|||
};
|
||||
|
||||
Memory.prototype.create = function create(model, data, options, callback) {
|
||||
var self = this;
|
||||
const self = this;
|
||||
this._createSync(model, data, function(err, id) {
|
||||
if (err) {
|
||||
return process.nextTick(function() {
|
||||
callback(err);
|
||||
});
|
||||
};
|
||||
}
|
||||
self.saveToFile(id, callback);
|
||||
});
|
||||
};
|
||||
|
||||
Memory.prototype.createAll = function create(model, dataArray, options, callback) {
|
||||
const returnArr = [];
|
||||
async.eachSeries(
|
||||
dataArray,
|
||||
(data, cb) => {
|
||||
this._createSync(model, data, (err, id) => {
|
||||
if (err) {
|
||||
return process.nextTick(function() {
|
||||
cb(err);
|
||||
});
|
||||
}
|
||||
const returnData = Object.assign({}, data);
|
||||
this.setIdValue(model, returnData, id);
|
||||
returnArr.push(returnData);
|
||||
this.saveToFile(id, cb);
|
||||
});
|
||||
},
|
||||
(err) => {
|
||||
callback(err, returnArr);
|
||||
},
|
||||
);
|
||||
};
|
||||
|
||||
Memory.prototype.updateOrCreate = function(model, data, options, callback) {
|
||||
var self = this;
|
||||
const self = this;
|
||||
this.exists(model, self.getIdValue(model, data), options, function(err, exists) {
|
||||
if (exists) {
|
||||
self.save(model, data, options, function(err, data) {
|
||||
|
@ -295,10 +320,10 @@ Memory.prototype.updateOrCreate = function(model, data, options, callback) {
|
|||
|
||||
Memory.prototype.patchOrCreateWithWhere =
|
||||
Memory.prototype.upsertWithWhere = function(model, where, data, options, callback) {
|
||||
var self = this;
|
||||
var primaryKey = this.idName(model);
|
||||
var filter = {where: where};
|
||||
var nodes = self._findAllSkippingIncludes(model, filter);
|
||||
const self = this;
|
||||
const primaryKey = this.idName(model);
|
||||
const filter = {where: where};
|
||||
const nodes = self._findAllSkippingIncludes(model, filter);
|
||||
if (nodes.length === 0) {
|
||||
return self._createSync(model, data, function(err, id) {
|
||||
if (err) return process.nextTick(function() { callback(err); });
|
||||
|
@ -309,13 +334,13 @@ Memory.prototype.upsertWithWhere = function(model, where, data, options, callbac
|
|||
});
|
||||
}
|
||||
if (nodes.length === 1) {
|
||||
var primaryKeyValue = nodes[0][primaryKey];
|
||||
const primaryKeyValue = nodes[0][primaryKey];
|
||||
self.updateAttributes(model, primaryKeyValue, data, options, function(err, data) {
|
||||
callback(err, data, {isNewInstance: false});
|
||||
});
|
||||
} else {
|
||||
process.nextTick(function() {
|
||||
var error = new Error('There are multiple instances found.' +
|
||||
const error = new Error('There are multiple instances found.' +
|
||||
'Upsert Operation will not be performed!');
|
||||
error.statusCode = 400;
|
||||
callback(error);
|
||||
|
@ -323,10 +348,10 @@ Memory.prototype.upsertWithWhere = function(model, where, data, options, callbac
|
|||
}
|
||||
};
|
||||
|
||||
Memory.prototype.findOrCreate = function(model, filter, data, callback) {
|
||||
var self = this;
|
||||
var nodes = self._findAllSkippingIncludes(model, filter);
|
||||
var found = nodes[0];
|
||||
Memory.prototype.findOrCreate = function(model, filter, data, options, callback) {
|
||||
const self = this;
|
||||
const nodes = self._findAllSkippingIncludes(model, filter);
|
||||
const found = nodes[0];
|
||||
|
||||
if (!found) {
|
||||
// Calling _createSync to update the collection in a sync way and to guarantee to create it in the same turn of even loop
|
||||
|
@ -345,7 +370,7 @@ Memory.prototype.findOrCreate = function(model, filter, data, callback) {
|
|||
});
|
||||
}
|
||||
|
||||
self._models[model].model.include(nodes[0], filter.include, {}, function(err, nodes) {
|
||||
self._models[model].model.include(nodes[0], filter.include, options, function(err, nodes) {
|
||||
process.nextTick(function() {
|
||||
if (err) return callback(err);
|
||||
callback(null, nodes[0], false);
|
||||
|
@ -354,10 +379,10 @@ Memory.prototype.findOrCreate = function(model, filter, data, callback) {
|
|||
};
|
||||
|
||||
Memory.prototype.save = function save(model, data, options, callback) {
|
||||
var self = this;
|
||||
var id = this.getIdValue(model, data);
|
||||
var cachedModels = this.collection(model);
|
||||
var modelData = cachedModels && this.collection(model)[id];
|
||||
const self = this;
|
||||
const id = this.getIdValue(model, data);
|
||||
const cachedModels = this.collection(model);
|
||||
let modelData = cachedModels && this.collection(model)[id];
|
||||
modelData = modelData && deserialize(modelData);
|
||||
if (modelData) {
|
||||
data = merge(modelData, data);
|
||||
|
@ -381,7 +406,7 @@ Memory.prototype.find = function find(model, id, options, callback) {
|
|||
};
|
||||
|
||||
Memory.prototype.destroy = function destroy(model, id, options, callback) {
|
||||
var exists = this.collection(model)[id];
|
||||
const exists = this.collection(model)[id];
|
||||
delete this.collection(model)[id];
|
||||
this.saveToFile({count: exists ? 1 : 0}, callback);
|
||||
};
|
||||
|
@ -389,9 +414,9 @@ Memory.prototype.destroy = function destroy(model, id, options, callback) {
|
|||
Memory.prototype.fromDb = function(model, data) {
|
||||
if (!data) return null;
|
||||
data = deserialize(data);
|
||||
var props = this._models[model].properties;
|
||||
for (var key in data) {
|
||||
var val = data[key];
|
||||
const props = this._models[model].properties;
|
||||
for (const key in data) {
|
||||
let val = data[key];
|
||||
if (val === undefined || val === null) {
|
||||
continue;
|
||||
}
|
||||
|
@ -417,9 +442,9 @@ function getValue(obj, path) {
|
|||
if (obj == null) {
|
||||
return undefined;
|
||||
}
|
||||
var keys = path.split('.');
|
||||
var val = obj;
|
||||
for (var i = 0, n = keys.length; i < n; i++) {
|
||||
const keys = path.split('.');
|
||||
let val = obj;
|
||||
for (let i = 0, n = keys.length; i < n; i++) {
|
||||
val = val[keys[i]];
|
||||
if (val == null) {
|
||||
return val;
|
||||
|
@ -429,26 +454,26 @@ function getValue(obj, path) {
|
|||
}
|
||||
|
||||
Memory.prototype._findAllSkippingIncludes = function(model, filter) {
|
||||
var nodes = Object.keys(this.collection(model)).map(function(key) {
|
||||
let nodes = Object.keys(this.collection(model)).map(function(key) {
|
||||
return this.fromDb(model, this.collection(model)[key]);
|
||||
}.bind(this));
|
||||
|
||||
if (filter) {
|
||||
if (!filter.order) {
|
||||
var idNames = this.idNames(model);
|
||||
const idNames = this.idNames(model);
|
||||
if (idNames && idNames.length) {
|
||||
filter.order = idNames;
|
||||
}
|
||||
}
|
||||
// do we need some sorting?
|
||||
if (filter.order) {
|
||||
var orders = filter.order;
|
||||
let orders = filter.order;
|
||||
if (typeof filter.order === 'string') {
|
||||
orders = [filter.order];
|
||||
}
|
||||
orders.forEach(function(key, i) {
|
||||
var reverse = 1;
|
||||
var m = key.match(/\s+(A|DE)SC$/i);
|
||||
let reverse = 1;
|
||||
const m = key.match(/\s+(A|DE)SC$/i);
|
||||
if (m) {
|
||||
key = key.replace(/\s+(A|DE)SC/i, '');
|
||||
if (m[1].toLowerCase() === 'de') reverse = -1;
|
||||
|
@ -458,7 +483,7 @@ Memory.prototype._findAllSkippingIncludes = function(model, filter) {
|
|||
nodes = nodes.sort(sorting.bind(orders));
|
||||
}
|
||||
|
||||
var nearFilter = geo.nearFilter(filter.where);
|
||||
const nearFilter = geo.nearFilter(filter.where);
|
||||
|
||||
// geo sorting
|
||||
if (nearFilter) {
|
||||
|
@ -475,18 +500,18 @@ Memory.prototype._findAllSkippingIncludes = function(model, filter) {
|
|||
}
|
||||
|
||||
// limit/skip
|
||||
var skip = filter.skip || filter.offset || 0;
|
||||
var limit = filter.limit || nodes.length;
|
||||
const skip = filter.skip || filter.offset || 0;
|
||||
const limit = filter.limit || nodes.length;
|
||||
nodes = nodes.slice(skip, skip + limit);
|
||||
}
|
||||
return nodes;
|
||||
|
||||
function sorting(a, b) {
|
||||
var undefinedA, undefinedB;
|
||||
let undefinedA, undefinedB;
|
||||
|
||||
for (var i = 0, l = this.length; i < l; i++) {
|
||||
var aVal = getValue(a, this[i].key);
|
||||
var bVal = getValue(b, this[i].key);
|
||||
for (let i = 0, l = this.length; i < l; i++) {
|
||||
const aVal = getValue(a, this[i].key);
|
||||
const bVal = getValue(b, this[i].key);
|
||||
undefinedB = bVal === undefined && aVal !== undefined;
|
||||
undefinedA = aVal === undefined && bVal !== undefined;
|
||||
|
||||
|
@ -502,8 +527,8 @@ Memory.prototype._findAllSkippingIncludes = function(model, filter) {
|
|||
};
|
||||
|
||||
Memory.prototype.all = function all(model, filter, options, callback) {
|
||||
var self = this;
|
||||
var nodes = self._findAllSkippingIncludes(model, filter);
|
||||
const self = this;
|
||||
const nodes = self._findAllSkippingIncludes(model, filter);
|
||||
|
||||
process.nextTick(function() {
|
||||
if (filter && filter.include) {
|
||||
|
@ -515,11 +540,11 @@ Memory.prototype.all = function all(model, filter, options, callback) {
|
|||
};
|
||||
|
||||
function applyFilter(filter) {
|
||||
var where = filter.where;
|
||||
const where = filter.where;
|
||||
if (typeof where === 'function') {
|
||||
return where;
|
||||
}
|
||||
var keys = Object.keys(where);
|
||||
const keys = Object.keys(where);
|
||||
return function(obj) {
|
||||
return keys.every(function(key) {
|
||||
if (key === 'and' || key === 'or') {
|
||||
|
@ -537,18 +562,18 @@ function applyFilter(filter) {
|
|||
}
|
||||
}
|
||||
|
||||
var value = getValue(obj, key);
|
||||
const value = getValue(obj, key);
|
||||
// Support referencesMany and other embedded relations
|
||||
// Also support array types. Mongo, possibly PostgreSQL
|
||||
if (Array.isArray(value)) {
|
||||
var matcher = where[key];
|
||||
const matcher = where[key];
|
||||
// The following condition is for the case where we are querying with
|
||||
// a neq filter, and when the value is an empty array ([]).
|
||||
if (matcher.neq !== undefined && value.length <= 0) {
|
||||
return true;
|
||||
}
|
||||
return value.some(function(v, i) {
|
||||
var filter = {where: {}};
|
||||
const filter = {where: {}};
|
||||
filter.where[i] = matcher;
|
||||
return applyFilter(filter)(value);
|
||||
});
|
||||
|
@ -560,11 +585,11 @@ function applyFilter(filter) {
|
|||
|
||||
// If we have a composed key a.b and b would resolve to a property of an object inside an array
|
||||
// then, we attempt to emulate mongo db matching. Helps for embedded relations
|
||||
var dotIndex = key.indexOf('.');
|
||||
var subValue = obj[key.substring(0, dotIndex)];
|
||||
const dotIndex = key.indexOf('.');
|
||||
const subValue = obj[key.substring(0, dotIndex)];
|
||||
if (dotIndex !== -1) {
|
||||
var subFilter = {where: {}};
|
||||
var subKey = key.substring(dotIndex + 1);
|
||||
const subFilter = {where: {}};
|
||||
const subKey = key.substring(dotIndex + 1);
|
||||
subFilter.where[subKey] = where[key];
|
||||
if (Array.isArray(subValue)) {
|
||||
return subValue.some(applyFilter(subFilter));
|
||||
|
@ -581,12 +606,12 @@ function applyFilter(filter) {
|
|||
if (pattern instanceof RegExp) {
|
||||
return pattern;
|
||||
}
|
||||
var regex = '';
|
||||
let regex = '';
|
||||
// Escaping user input to be treated as a literal string within a regular expression
|
||||
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions#Writing_a_Regular_Expression_Pattern
|
||||
pattern = pattern.replace(/([.*+?^=!:${}()|\[\]\/\\])/g, '\\$1');
|
||||
for (var i = 0, n = pattern.length; i < n; i++) {
|
||||
var char = pattern.charAt(i);
|
||||
for (let i = 0, n = pattern.length; i < n; i++) {
|
||||
const char = pattern.charAt(i);
|
||||
if (char === '\\') {
|
||||
i++; // Skip to next char
|
||||
if (i < n) {
|
||||
|
@ -613,8 +638,8 @@ function applyFilter(filter) {
|
|||
return value.match(example);
|
||||
}
|
||||
|
||||
if (example === undefined) {
|
||||
return undefined;
|
||||
if (example == null) {
|
||||
return value == null;
|
||||
}
|
||||
|
||||
if (typeof example === 'object' && example !== null) {
|
||||
|
@ -627,7 +652,7 @@ function applyFilter(filter) {
|
|||
return true;
|
||||
}
|
||||
|
||||
var i;
|
||||
let i;
|
||||
if (example.inq) {
|
||||
// if (!value) return false;
|
||||
for (i = 0; i < example.inq.length; i++) {
|
||||
|
@ -657,7 +682,7 @@ function applyFilter(filter) {
|
|||
}
|
||||
|
||||
if (example.like || example.nlike || example.ilike || example.nilike) {
|
||||
var like = example.like || example.nlike || example.ilike || example.nilike;
|
||||
let like = example.like || example.nlike || example.ilike || example.nilike;
|
||||
if (typeof like === 'string') {
|
||||
like = toRegExp(like);
|
||||
}
|
||||
|
@ -682,9 +707,13 @@ function applyFilter(filter) {
|
|||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// compare date
|
||||
if (example instanceof Date && value instanceof Date) {
|
||||
return example.getTime() === value.getTime();
|
||||
}
|
||||
// not strict equality
|
||||
return (example !== null ? example.toString() : example) ==
|
||||
(value != null ? value.toString() : value);
|
||||
return example == value;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -709,7 +738,7 @@ function applyFilter(filter) {
|
|||
return val1 - val2;
|
||||
}
|
||||
if (val1 instanceof Date) {
|
||||
var result = val1 - val2;
|
||||
const result = val1 - val2;
|
||||
return result;
|
||||
}
|
||||
// Return NaN if we don't know how to compare
|
||||
|
@ -734,9 +763,9 @@ function applyFilter(filter) {
|
|||
}
|
||||
|
||||
Memory.prototype.destroyAll = function destroyAll(model, where, options, callback) {
|
||||
var cache = this.collection(model);
|
||||
var filter = null;
|
||||
var count = 0;
|
||||
const cache = this.collection(model);
|
||||
let filter = null;
|
||||
let count = 0;
|
||||
if (where) {
|
||||
filter = applyFilter({where: where});
|
||||
Object.keys(cache).forEach(function(id) {
|
||||
|
@ -753,10 +782,10 @@ Memory.prototype.destroyAll = function destroyAll(model, where, options, callbac
|
|||
};
|
||||
|
||||
Memory.prototype.count = function count(model, where, options, callback) {
|
||||
var cache = this.collection(model);
|
||||
var data = Object.keys(cache);
|
||||
const cache = this.collection(model);
|
||||
let data = Object.keys(cache);
|
||||
if (where) {
|
||||
var filter = {where: where};
|
||||
const filter = {where: where};
|
||||
data = data.map(function(id) {
|
||||
return this.fromDb(model, cache[id]);
|
||||
}.bind(this));
|
||||
|
@ -769,16 +798,16 @@ Memory.prototype.count = function count(model, where, options, callback) {
|
|||
|
||||
Memory.prototype.update =
|
||||
Memory.prototype.updateAll = function updateAll(model, where, data, options, cb) {
|
||||
var self = this;
|
||||
var cache = this.collection(model);
|
||||
var filter = null;
|
||||
const self = this;
|
||||
const cache = this.collection(model);
|
||||
let filter = null;
|
||||
where = where || {};
|
||||
filter = applyFilter({where: where});
|
||||
|
||||
var ids = Object.keys(cache);
|
||||
var count = 0;
|
||||
const ids = Object.keys(cache);
|
||||
let count = 0;
|
||||
async.each(ids, function(id, done) {
|
||||
var inst = self.fromDb(model, cache[id]);
|
||||
const inst = self.fromDb(model, cache[id]);
|
||||
if (!filter || filter(inst)) {
|
||||
count++;
|
||||
// The id value from the cache is string
|
||||
|
@ -796,7 +825,7 @@ Memory.prototype.update =
|
|||
|
||||
Memory.prototype.updateAttributes = function updateAttributes(model, id, data, options, cb) {
|
||||
if (!id) {
|
||||
var err = new Error(g.f('You must provide an {{id}} when updating attributes!'));
|
||||
const err = new Error(g.f('You must provide an {{id}} when updating attributes!'));
|
||||
if (cb) {
|
||||
return cb(err);
|
||||
} else {
|
||||
|
@ -809,40 +838,40 @@ Memory.prototype.updateAttributes = function updateAttributes(model, id, data, o
|
|||
|
||||
this.setIdValue(model, data, id);
|
||||
|
||||
var cachedModels = this.collection(model);
|
||||
var modelData = cachedModels && this.collection(model)[id];
|
||||
const cachedModels = this.collection(model);
|
||||
const modelData = cachedModels && this.collection(model)[id];
|
||||
|
||||
if (modelData) {
|
||||
this.save(model, data, options, cb);
|
||||
} else {
|
||||
var msg = g.f('Could not update attributes. {{Object}} with {{id}} %s does not exist!', id);
|
||||
var error = new Error(msg);
|
||||
const msg = g.f('Could not update attributes. {{Object}} with {{id}} %s does not exist!', id);
|
||||
const error = new Error(msg);
|
||||
error.statusCode = error.status = 404;
|
||||
cb(error);
|
||||
}
|
||||
};
|
||||
|
||||
Memory.prototype.replaceById = function(model, id, data, options, cb) {
|
||||
var self = this;
|
||||
const self = this;
|
||||
if (!id) {
|
||||
var err = new Error(g.f('You must provide an {{id}} when replacing!'));
|
||||
const err = new Error(g.f('You must provide an {{id}} when replacing!'));
|
||||
return process.nextTick(function() { cb(err); });
|
||||
}
|
||||
// Do not modify the data object passed in arguments
|
||||
data = Object.create(data);
|
||||
this.setIdValue(model, data, id);
|
||||
var cachedModels = this.collection(model);
|
||||
var modelData = cachedModels && this.collection(model)[id];
|
||||
const cachedModels = this.collection(model);
|
||||
const modelData = cachedModels && this.collection(model)[id];
|
||||
if (!modelData) {
|
||||
var msg = 'Could not replace. Object with id ' + id + ' does not exist!';
|
||||
var error = new Error(msg);
|
||||
const msg = 'Could not replace. Object with id ' + id + ' does not exist!';
|
||||
const error = new Error(msg);
|
||||
error.statusCode = error.status = 404;
|
||||
return process.nextTick(function() { cb(error); });
|
||||
}
|
||||
|
||||
var newModelData = {};
|
||||
for (var key in data) {
|
||||
var val = data[key];
|
||||
const newModelData = {};
|
||||
for (const key in data) {
|
||||
const val = data[key];
|
||||
if (typeof val === 'function') {
|
||||
continue; // Skip methods
|
||||
}
|
||||
|
@ -856,13 +885,13 @@ Memory.prototype.replaceById = function(model, id, data, options, cb) {
|
|||
};
|
||||
|
||||
Memory.prototype.replaceOrCreate = function(model, data, options, callback) {
|
||||
var self = this;
|
||||
var idName = self.idNames(model)[0];
|
||||
var idValue = self.getIdValue(model, data);
|
||||
var filter = {where: {}};
|
||||
const self = this;
|
||||
const idName = self.idNames(model)[0];
|
||||
const idValue = self.getIdValue(model, data);
|
||||
const filter = {where: {}};
|
||||
filter.where[idName] = idValue;
|
||||
var nodes = self._findAllSkippingIncludes(model, filter);
|
||||
var found = nodes[0];
|
||||
const nodes = self._findAllSkippingIncludes(model, filter);
|
||||
const found = nodes[0];
|
||||
|
||||
if (!found) {
|
||||
// Calling _createSync to update the collection in a sync way and
|
||||
|
@ -875,7 +904,7 @@ Memory.prototype.replaceOrCreate = function(model, data, options, callback) {
|
|||
});
|
||||
});
|
||||
}
|
||||
var id = self.getIdValue(model, data);
|
||||
const id = self.getIdValue(model, data);
|
||||
self.collection(model)[id] = serialize(data);
|
||||
self.saveToFile(data, function(err) {
|
||||
callback(err, self.fromDb(model, data), {isNewInstance: false});
|
||||
|
@ -896,7 +925,7 @@ Memory.prototype.buildNearFilter = function(filter) {
|
|||
};
|
||||
|
||||
Memory.prototype.automigrate = function(models, cb) {
|
||||
var self = this;
|
||||
const self = this;
|
||||
|
||||
if ((!cb) && ('function' === typeof models)) {
|
||||
cb = models;
|
||||
|
@ -912,7 +941,7 @@ Memory.prototype.automigrate = function(models, cb) {
|
|||
return process.nextTick(cb);
|
||||
}
|
||||
|
||||
var invalidModels = models.filter(function(m) {
|
||||
const invalidModels = models.filter(function(m) {
|
||||
return !(m in self._models);
|
||||
});
|
||||
|
||||
|
@ -935,8 +964,8 @@ function merge(base, update) {
|
|||
}
|
||||
// We cannot use Object.keys(update) if the update is an instance of the model
|
||||
// class as the properties are defined at the ModelClass.prototype level
|
||||
for (var key in update) {
|
||||
var val = update[key];
|
||||
for (const key in update) {
|
||||
const val = update[key];
|
||||
if (typeof val === 'function') {
|
||||
continue; // Skip methods
|
||||
}
|
||||
|
|
|
@ -1,15 +1,15 @@
|
|||
// Copyright IBM Corp. 2014,2016. All Rights Reserved.
|
||||
// Copyright IBM Corp. 2014,2019. All Rights Reserved.
|
||||
// Node module: loopback-datasource-juggler
|
||||
// This file is licensed under the MIT License.
|
||||
// License text available at https://opensource.org/licenses/MIT
|
||||
|
||||
'use strict';
|
||||
|
||||
var g = require('strong-globalize')();
|
||||
var util = require('util');
|
||||
var Connector = require('loopback-connector').Connector;
|
||||
var utils = require('../utils');
|
||||
var crypto = require('crypto');
|
||||
const g = require('strong-globalize')();
|
||||
const util = require('util');
|
||||
const Connector = require('loopback-connector').Connector;
|
||||
const utils = require('../utils');
|
||||
const crypto = require('crypto');
|
||||
|
||||
/**
|
||||
* Initialize the Transient connector against the given data source
|
||||
|
@ -59,16 +59,16 @@ Transient.prototype.connect = function(callback) {
|
|||
};
|
||||
|
||||
Transient.prototype.generateId = function(model, data, idName) {
|
||||
var idType;
|
||||
var props = this._models[model].properties;
|
||||
let idType;
|
||||
const props = this._models[model].properties;
|
||||
if (idName) idType = props[idName] && props[idName].type;
|
||||
idType = idType || this.getDefaultIdType();
|
||||
if (idType === Number) {
|
||||
return Math.floor(Math.random() * 10000); // max. 4 digits
|
||||
} else {
|
||||
return crypto.randomBytes(Math.ceil(24 / 2))
|
||||
.toString('hex') // convert to hexadecimal format
|
||||
.slice(0, 24); // return required number of characters
|
||||
.toString('hex') // convert to hexadecimal format
|
||||
.slice(0, 24); // return required number of characters
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -89,10 +89,11 @@ Transient.prototype.count = function count(model, callback, where) {
|
|||
};
|
||||
|
||||
Transient.prototype.create = function create(model, data, callback) {
|
||||
var props = this._models[model].properties;
|
||||
var idName = this.idName(model);
|
||||
const props = this._models[model].properties;
|
||||
const idName = this.idName(model);
|
||||
let id = undefined;
|
||||
if (idName && props[idName]) {
|
||||
var id = this.getIdValue(model, data) || this.generateId(model, data, idName);
|
||||
id = this.getIdValue(model, data) || this.generateId(model, data, idName);
|
||||
id = (props[idName] && props[idName].type && props[idName].type(id)) || id;
|
||||
this.setIdValue(model, data, id);
|
||||
}
|
||||
|
@ -105,13 +106,13 @@ Transient.prototype.save = function save(model, data, callback) {
|
|||
|
||||
Transient.prototype.update =
|
||||
Transient.prototype.updateAll = function updateAll(model, where, data, cb) {
|
||||
var count = 0;
|
||||
const count = 0;
|
||||
this.flush('update', {count: count}, cb);
|
||||
};
|
||||
|
||||
Transient.prototype.updateAttributes = function updateAttributes(model, id, data, cb) {
|
||||
if (!id) {
|
||||
var err = new Error(g.f('You must provide an {{id}} when updating attributes!'));
|
||||
const err = new Error(g.f('You must provide an {{id}} when updating attributes!'));
|
||||
if (cb) {
|
||||
return cb(err);
|
||||
} else {
|
||||
|
|
1430
lib/dao.js
1430
lib/dao.js
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -1,11 +1,11 @@
|
|||
// Copyright IBM Corp. 2013,2016. All Rights Reserved.
|
||||
// Copyright IBM Corp. 2017,2019. All Rights Reserved.
|
||||
// Node module: loopback-datasource-juggler
|
||||
// This file is licensed under the MIT License.
|
||||
// License text available at https://opensource.org/licenses/MIT
|
||||
|
||||
'use strict';
|
||||
|
||||
var inspect = require('util').inspect;
|
||||
const inspect = require('util').inspect;
|
||||
|
||||
module.exports = DateString;
|
||||
|
||||
|
@ -53,6 +53,10 @@ function DateString(value) {
|
|||
return new DateString(value);
|
||||
}
|
||||
|
||||
if (value instanceof DateString) {
|
||||
value = value.when;
|
||||
}
|
||||
|
||||
if (typeof(value) !== 'string') {
|
||||
throw new Error('Input must be a string');
|
||||
}
|
||||
|
@ -60,7 +64,7 @@ function DateString(value) {
|
|||
Object.defineProperty(this, 'when', {
|
||||
get: () => { return this._when; },
|
||||
set: (val) => {
|
||||
var d = new Date(val);
|
||||
const d = new Date(val);
|
||||
if (isNaN(d.getTime())) {
|
||||
throw new Error('Invalid date');
|
||||
} else {
|
||||
|
@ -71,7 +75,7 @@ function DateString(value) {
|
|||
});
|
||||
|
||||
this.when = value;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the value of DateString in its original form.
|
||||
|
@ -97,3 +101,10 @@ DateString.prototype.inspect = function(depth, options) {
|
|||
_date: this._date,
|
||||
});
|
||||
};
|
||||
|
||||
if (inspect.custom) {
|
||||
// Node.js 12+ no longer recognizes "inspect" method,
|
||||
// it uses "inspect.custom" symbol as the key instead
|
||||
// TODO(semver-major) always use the symbol key only (requires Node.js 8+).
|
||||
DateString.prototype[inspect.custom] = DateString.prototype.inspect;
|
||||
}
|
||||
|
|
64
lib/geo.js
64
lib/geo.js
|
@ -1,17 +1,21 @@
|
|||
// Copyright IBM Corp. 2013,2016. All Rights Reserved.
|
||||
// Copyright IBM Corp. 2013,2019. All Rights Reserved.
|
||||
// Node module: loopback-datasource-juggler
|
||||
// This file is licensed under the MIT License.
|
||||
// License text available at https://opensource.org/licenses/MIT
|
||||
|
||||
'use strict';
|
||||
|
||||
var assert = require('assert');
|
||||
const assert = require('assert');
|
||||
|
||||
/*!
|
||||
* Get a near filter from a given where object. For connector use only.
|
||||
*/
|
||||
|
||||
exports.nearFilter = function nearFilter(where) {
|
||||
const nearResults = [];
|
||||
nearSearch(where);
|
||||
return (!nearResults.length ? false : nearResults);
|
||||
|
||||
function nearSearch(clause, parentKeys) {
|
||||
if (typeof clause !== 'object') {
|
||||
return false;
|
||||
|
@ -22,12 +26,12 @@ exports.nearFilter = function nearFilter(where) {
|
|||
if (typeof clause[clauseKey] !== 'object' || !clause[clauseKey]) return;
|
||||
if (Array.isArray(clause[clauseKey])) {
|
||||
clause[clauseKey].forEach(function(el, index) {
|
||||
var ret = nearSearch(el, parentKeys.concat(clauseKey).concat(index));
|
||||
const ret = nearSearch(el, parentKeys.concat(clauseKey).concat(index));
|
||||
if (ret) return ret;
|
||||
});
|
||||
} else {
|
||||
if (clause[clauseKey].hasOwnProperty('near')) {
|
||||
var result = clause[clauseKey];
|
||||
const result = clause[clauseKey];
|
||||
nearResults.push({
|
||||
near: result.near,
|
||||
maxDistance: result.maxDistance,
|
||||
|
@ -38,13 +42,9 @@ exports.nearFilter = function nearFilter(where) {
|
|||
key: clauseKey,
|
||||
});
|
||||
}
|
||||
};
|
||||
}
|
||||
});
|
||||
}
|
||||
var nearResults = [];
|
||||
nearSearch(where);
|
||||
|
||||
return (!nearResults.length ? false : nearResults);
|
||||
};
|
||||
|
||||
/*!
|
||||
|
@ -68,19 +68,19 @@ exports.nearFilter = function nearFilter(where) {
|
|||
*/
|
||||
|
||||
exports.filter = function(rawResults, filters) {
|
||||
var distances = {};
|
||||
var results = [];
|
||||
const distances = {};
|
||||
const results = [];
|
||||
|
||||
filters.forEach(function(filter) {
|
||||
var origin = filter.near;
|
||||
var max = filter.maxDistance > 0 ? filter.maxDistance : false;
|
||||
var min = filter.minDistance > 0 ? filter.minDistance : false;
|
||||
var unit = filter.unit;
|
||||
var key = filter.key;
|
||||
const origin = filter.near;
|
||||
const max = filter.maxDistance > 0 ? filter.maxDistance : false;
|
||||
const min = filter.minDistance > 0 ? filter.minDistance : false;
|
||||
const unit = filter.unit;
|
||||
const key = filter.key;
|
||||
|
||||
// create distance index
|
||||
rawResults.forEach(function(result) {
|
||||
var loc = result[key];
|
||||
let loc = result[key];
|
||||
|
||||
// filter out results without locations
|
||||
if (!loc) return;
|
||||
|
@ -90,7 +90,7 @@ exports.filter = function(rawResults, filters) {
|
|||
if (typeof loc.lat !== 'number') return;
|
||||
if (typeof loc.lng !== 'number') return;
|
||||
|
||||
var d = GeoPoint.distanceBetween(origin, loc, {type: unit});
|
||||
const d = GeoPoint.distanceBetween(origin, loc, {type: unit});
|
||||
|
||||
// filter result if distance is either < minDistance or > maxDistance
|
||||
if ((min && d < min) || (max && d > max)) return;
|
||||
|
@ -100,12 +100,12 @@ exports.filter = function(rawResults, filters) {
|
|||
});
|
||||
|
||||
results.sort(function(resA, resB) {
|
||||
var a = resA[key];
|
||||
var b = resB[key];
|
||||
const a = resA[key];
|
||||
const b = resB[key];
|
||||
|
||||
if (a && b) {
|
||||
var da = distances[resA.id];
|
||||
var db = distances[resB.id];
|
||||
const da = distances[resA.id];
|
||||
const db = distances[resB.id];
|
||||
|
||||
if (db === da) return 0;
|
||||
return da > db ? 1 : -1;
|
||||
|
@ -231,11 +231,11 @@ GeoPoint.distanceBetween = function distanceBetween(a, b, options) {
|
|||
b = GeoPoint(b);
|
||||
}
|
||||
|
||||
var x1 = a.lat;
|
||||
var y1 = a.lng;
|
||||
const x1 = a.lat;
|
||||
const y1 = a.lng;
|
||||
|
||||
var x2 = b.lat;
|
||||
var y2 = b.lng;
|
||||
const x2 = b.lat;
|
||||
const y2 = b.lng;
|
||||
|
||||
return geoDistance(x1, y1, x2, y2, options);
|
||||
};
|
||||
|
@ -283,13 +283,13 @@ GeoPoint.prototype.toString = function() {
|
|||
*/
|
||||
|
||||
// factor to convert degrees to radians
|
||||
var DEG2RAD = 0.01745329252;
|
||||
const DEG2RAD = 0.01745329252;
|
||||
|
||||
// factor to convert radians degrees to degrees
|
||||
var RAD2DEG = 57.29577951308;
|
||||
const RAD2DEG = 57.29577951308;
|
||||
|
||||
// radius of the earth
|
||||
var EARTH_RADIUS = {
|
||||
const EARTH_RADIUS = {
|
||||
kilometers: 6370.99056,
|
||||
meters: 6370990.56,
|
||||
miles: 3958.75,
|
||||
|
@ -299,7 +299,7 @@ var EARTH_RADIUS = {
|
|||
};
|
||||
|
||||
function geoDistance(x1, y1, x2, y2, options) {
|
||||
var type = (options && options.type) || 'miles';
|
||||
const type = (options && options.type) || 'miles';
|
||||
|
||||
// Convert to radians
|
||||
x1 = x1 * DEG2RAD;
|
||||
|
@ -309,11 +309,11 @@ function geoDistance(x1, y1, x2, y2, options) {
|
|||
|
||||
// use the haversine formula to calculate distance for any 2 points on a sphere.
|
||||
// ref http://en.wikipedia.org/wiki/Haversine_formula
|
||||
var haversine = function(a) {
|
||||
const haversine = function(a) {
|
||||
return Math.pow(Math.sin(a / 2.0), 2);
|
||||
};
|
||||
|
||||
var f = Math.sqrt(haversine(x2 - x1) + Math.cos(x2) * Math.cos(x1) * haversine(y2 - y1));
|
||||
const f = Math.sqrt(haversine(x2 - x1) + Math.cos(x2) * Math.cos(x1) * haversine(y2 - y1));
|
||||
|
||||
return 2 * Math.asin(f) * EARTH_RADIUS[type];
|
||||
}
|
||||
|
|
20
lib/hooks.js
20
lib/hooks.js
|
@ -1,12 +1,12 @@
|
|||
// Copyright IBM Corp. 2013,2016. All Rights Reserved.
|
||||
// Copyright IBM Corp. 2013,2019. All Rights Reserved.
|
||||
// Node module: loopback-datasource-juggler
|
||||
// This file is licensed under the MIT License.
|
||||
// License text available at https://opensource.org/licenses/MIT
|
||||
|
||||
'use strict';
|
||||
|
||||
var deprecated = require('depd')('loopback-datasource-juggler');
|
||||
var g = require('strong-globalize')();
|
||||
const deprecated = require('depd')('loopback-datasource-juggler');
|
||||
const g = require('strong-globalize')();
|
||||
|
||||
/*!
|
||||
* Module exports
|
||||
|
@ -48,16 +48,16 @@ Hookable.afterDestroy = null;
|
|||
* @param {Function} callback
|
||||
*/
|
||||
Hookable.prototype.trigger = function trigger(actionName, work, data, callback) {
|
||||
var capitalizedName = capitalize(actionName);
|
||||
var beforeHook = this.constructor['before' + capitalizedName] ||
|
||||
const capitalizedName = capitalize(actionName);
|
||||
let beforeHook = this.constructor['before' + capitalizedName] ||
|
||||
this.constructor['pre' + capitalizedName];
|
||||
var afterHook = this.constructor['after' + capitalizedName] ||
|
||||
let afterHook = this.constructor['after' + capitalizedName] ||
|
||||
this.constructor['post' + capitalizedName];
|
||||
if (actionName === 'validate') {
|
||||
beforeHook = beforeHook || this.constructor.beforeValidation;
|
||||
afterHook = afterHook || this.constructor.afterValidation;
|
||||
}
|
||||
var inst = this;
|
||||
const inst = this;
|
||||
|
||||
if (actionName !== 'initialize') {
|
||||
if (beforeHook)
|
||||
|
@ -100,13 +100,13 @@ function capitalize(string) {
|
|||
}
|
||||
|
||||
function deprecateHook(ctor, prefixes, capitalizedName) {
|
||||
var candidateNames = prefixes.map(function(p) { return p + capitalizedName; });
|
||||
const candidateNames = prefixes.map(function(p) { return p + capitalizedName; });
|
||||
if (capitalizedName === 'Validate')
|
||||
candidateNames.push(prefixes[0] + 'Validation');
|
||||
|
||||
var hookName = candidateNames.filter(function(hook) { return !!ctor[hook]; })[0];
|
||||
let hookName = candidateNames.filter(function(hook) { return !!ctor[hook]; })[0];
|
||||
if (!hookName) return; // just to be sure, this should never happen
|
||||
if (ctor.modelName) hookName = ctor.modelName + '.' + hookName;
|
||||
if (ctor.modelName) hookName = ctor.modelName + '.' + hookName;
|
||||
deprecated(g.f('Model hook "%s" is deprecated, ' +
|
||||
'use Operation hooks instead. ' +
|
||||
'{{http://docs.strongloop.com/display/LB/Operation+hooks}}', hookName));
|
||||
|
|
278
lib/include.js
278
lib/include.js
|
@ -1,19 +1,22 @@
|
|||
// Copyright IBM Corp. 2013,2015. All Rights Reserved.
|
||||
// Copyright IBM Corp. 2013,2019. All Rights Reserved.
|
||||
// Node module: loopback-datasource-juggler
|
||||
// This file is licensed under the MIT License.
|
||||
// License text available at https://opensource.org/licenses/MIT
|
||||
|
||||
'use strict';
|
||||
|
||||
var async = require('async');
|
||||
var g = require('strong-globalize')();
|
||||
var utils = require('./utils');
|
||||
var List = require('./list');
|
||||
var includeUtils = require('./include_utils');
|
||||
var isPlainObject = utils.isPlainObject;
|
||||
var defineCachedRelations = utils.defineCachedRelations;
|
||||
var uniq = utils.uniq;
|
||||
var idName = utils.idName;
|
||||
const async = require('async');
|
||||
const g = require('strong-globalize')();
|
||||
const utils = require('./utils');
|
||||
const List = require('./list');
|
||||
const includeUtils = require('./include_utils');
|
||||
const isPlainObject = utils.isPlainObject;
|
||||
const defineCachedRelations = utils.defineCachedRelations;
|
||||
const uniq = utils.uniq;
|
||||
const idName = utils.idName;
|
||||
const debug = require('debug')('loopback:include');
|
||||
|
||||
const DISALLOWED_TYPES = ['boolean', 'number', 'symbol', 'function'];
|
||||
|
||||
/*!
|
||||
* Normalize the include to be an array
|
||||
|
@ -21,19 +24,19 @@ var idName = utils.idName;
|
|||
* @returns {*}
|
||||
*/
|
||||
function normalizeInclude(include) {
|
||||
var newInclude;
|
||||
let newInclude;
|
||||
if (typeof include === 'string') {
|
||||
return [include];
|
||||
} else if (isPlainObject(include)) {
|
||||
// Build an array of key/value pairs
|
||||
newInclude = [];
|
||||
var rel = include.rel || include.relation;
|
||||
var obj = {};
|
||||
const rel = include.rel || include.relation;
|
||||
const obj = {};
|
||||
if (typeof rel === 'string') {
|
||||
obj[rel] = new IncludeScope(include.scope);
|
||||
newInclude.push(obj);
|
||||
} else {
|
||||
for (var key in include) {
|
||||
for (const key in include) {
|
||||
obj[key] = include[key];
|
||||
newInclude.push(obj);
|
||||
}
|
||||
|
@ -41,11 +44,14 @@ function normalizeInclude(include) {
|
|||
return newInclude;
|
||||
} else if (Array.isArray(include)) {
|
||||
newInclude = [];
|
||||
for (var i = 0, n = include.length; i < n; i++) {
|
||||
var subIncludes = normalizeInclude(include[i]);
|
||||
for (let i = 0, n = include.length; i < n; i++) {
|
||||
const subIncludes = normalizeInclude(include[i]);
|
||||
newInclude = newInclude.concat(subIncludes);
|
||||
}
|
||||
return newInclude;
|
||||
} else if (DISALLOWED_TYPES.includes(typeof include)) {
|
||||
debug('Ignoring invalid "include" value of type %s:', typeof include, include);
|
||||
return [];
|
||||
} else {
|
||||
return include;
|
||||
}
|
||||
|
@ -79,8 +85,8 @@ function lookupModel(models, modelName) {
|
|||
if (models[modelName]) {
|
||||
return models[modelName];
|
||||
}
|
||||
var lookupClassName = modelName.toLowerCase();
|
||||
for (var name in models) {
|
||||
const lookupClassName = modelName.toLowerCase();
|
||||
for (const name in models) {
|
||||
if (name.toLowerCase() === lookupClassName) {
|
||||
return models[name];
|
||||
}
|
||||
|
@ -160,7 +166,7 @@ Inclusion.include = function(objects, include, options, cb) {
|
|||
cb = options;
|
||||
options = {};
|
||||
}
|
||||
var self = this;
|
||||
const self = this;
|
||||
|
||||
if (!include || (Array.isArray(include) && include.length === 0) ||
|
||||
(Array.isArray(objects) && objects.length === 0) ||
|
||||
|
@ -172,17 +178,24 @@ Inclusion.include = function(objects, include, options, cb) {
|
|||
}
|
||||
|
||||
include = normalizeInclude(include);
|
||||
debug('include: %j', include);
|
||||
|
||||
// Find the limit of items for `inq`
|
||||
var inqLimit = 256;
|
||||
let inqLimit = 256;
|
||||
if (self.dataSource && self.dataSource.settings &&
|
||||
self.dataSource.settings.inqLimit) {
|
||||
inqLimit = self.dataSource.settings.inqLimit;
|
||||
}
|
||||
|
||||
async.each(include, function(item, callback) {
|
||||
processIncludeItem(objects, item, options, callback);
|
||||
try {
|
||||
processIncludeItem(objects, item, options, callback);
|
||||
} catch (err) {
|
||||
// async does not catch the error and report to the outer callback
|
||||
callback(err);
|
||||
}
|
||||
}, function(err) {
|
||||
debug(err, objects);
|
||||
cb && cb(err, objects);
|
||||
});
|
||||
|
||||
|
@ -196,13 +209,21 @@ Inclusion.include = function(objects, include, options, cb) {
|
|||
* @param cb
|
||||
*/
|
||||
function findWithForeignKeysByPage(model, filter, fkName, pageSize, options, cb) {
|
||||
var foreignKeys = [];
|
||||
try {
|
||||
const opts = Object.assign({prohibitProtectedPropertiesInQuery: true}, options);
|
||||
model._sanitizeQuery(filter.where, opts);
|
||||
model._coerce(filter.where, options);
|
||||
} catch (e) {
|
||||
return cb(e);
|
||||
}
|
||||
|
||||
let foreignKeys = [];
|
||||
if (filter.where[fkName]) {
|
||||
foreignKeys = filter.where[fkName].inq;
|
||||
} else if (filter.where.and) {
|
||||
// The inq can be embedded inside 'and: []'. No or: [] is needed as
|
||||
// include only uses and. We only deal with the generated inq for include.
|
||||
for (var j in filter.where.and) {
|
||||
for (const j in filter.where.and) {
|
||||
if (filter.where.and[j][fkName] &&
|
||||
Array.isArray(filter.where.and[j][fkName].inq)) {
|
||||
foreignKeys = filter.where.and[j][fkName].inq;
|
||||
|
@ -217,7 +238,7 @@ Inclusion.include = function(objects, include, options, cb) {
|
|||
// Force the find to be performed per FK to honor the pagination
|
||||
pageSize = 1;
|
||||
}
|
||||
var size = foreignKeys.length;
|
||||
const size = foreignKeys.length;
|
||||
if (size > inqLimit && pageSize <= 0) {
|
||||
pageSize = inqLimit;
|
||||
}
|
||||
|
@ -225,29 +246,29 @@ Inclusion.include = function(objects, include, options, cb) {
|
|||
return model.find(filter, options, cb);
|
||||
}
|
||||
|
||||
var listOfFKs = [];
|
||||
let listOfFKs = [];
|
||||
|
||||
for (var i = 0; i < size; i += pageSize) {
|
||||
var end = i + pageSize;
|
||||
for (let i = 0; i < size; i += pageSize) {
|
||||
let end = i + pageSize;
|
||||
if (end > size) {
|
||||
end = size;
|
||||
}
|
||||
listOfFKs.push(foreignKeys.slice(i, end));
|
||||
}
|
||||
|
||||
var items = [];
|
||||
let items = [];
|
||||
// Optimization: no need to resolve keys that are an empty array
|
||||
listOfFKs = listOfFKs.filter(function(keys) {
|
||||
return keys.length > 0;
|
||||
});
|
||||
async.each(listOfFKs, function(foreignKeys, done) {
|
||||
var newFilter = {};
|
||||
for (var f in filter) {
|
||||
const newFilter = {};
|
||||
for (const f in filter) {
|
||||
newFilter[f] = filter[f];
|
||||
}
|
||||
if (filter.where) {
|
||||
newFilter.where = {};
|
||||
for (var w in filter.where) {
|
||||
for (const w in filter.where) {
|
||||
newFilter.where[w] = filter.where[w];
|
||||
}
|
||||
}
|
||||
|
@ -266,10 +287,10 @@ Inclusion.include = function(objects, include, options, cb) {
|
|||
}
|
||||
|
||||
function processIncludeItem(objs, include, options, cb) {
|
||||
var relations = self.relations;
|
||||
const relations = self.relations;
|
||||
|
||||
var relationName;
|
||||
var subInclude = null, scope = null;
|
||||
let relationName;
|
||||
let subInclude = null, scope = null;
|
||||
|
||||
if (isPlainObject(include)) {
|
||||
relationName = Object.keys(include)[0];
|
||||
|
@ -288,12 +309,13 @@ Inclusion.include = function(objects, include, options, cb) {
|
|||
subInclude = null;
|
||||
}
|
||||
|
||||
var relation = relations[relationName];
|
||||
const relation = relations[relationName];
|
||||
if (!relation) {
|
||||
cb(new Error(g.f('Relation "%s" is not defined for %s model', relationName, self.modelName)));
|
||||
return;
|
||||
}
|
||||
var polymorphic = relation.polymorphic;
|
||||
debug('Relation: %j', relation);
|
||||
const polymorphic = relation.polymorphic;
|
||||
// if (polymorphic && !polymorphic.discriminator) {
|
||||
// cb(new Error('Relation "' + relationName + '" is polymorphic but ' +
|
||||
// 'discriminator is not present'));
|
||||
|
@ -309,18 +331,19 @@ Inclusion.include = function(objects, include, options, cb) {
|
|||
|
||||
// Just skip if inclusion is disabled
|
||||
if (relation.options.disableInclude) {
|
||||
debug('Relation is disabled from include', relation);
|
||||
return cb();
|
||||
}
|
||||
// prepare filter and fields for making DB Call
|
||||
var filter = (scope && scope.conditions()) || {};
|
||||
const filter = (scope && scope.conditions()) || {};
|
||||
if ((relation.multiple || relation.type === 'belongsTo') && scope) {
|
||||
var includeScope = {};
|
||||
const includeScope = {};
|
||||
// make sure not to miss any fields for sub includes
|
||||
if (filter.fields && Array.isArray(subInclude) &&
|
||||
relation.modelTo.relations) {
|
||||
includeScope.fields = [];
|
||||
subInclude.forEach(function(name) {
|
||||
var rel = relation.modelTo.relations[name];
|
||||
const rel = relation.modelTo.relations[name];
|
||||
if (rel && rel.type === 'belongsTo') {
|
||||
includeScope.fields.push(rel.keyFrom);
|
||||
}
|
||||
|
@ -331,7 +354,7 @@ Inclusion.include = function(objects, include, options, cb) {
|
|||
// Let's add a placeholder where query
|
||||
filter.where = filter.where || {};
|
||||
// if fields are specified, make sure target foreign key is present
|
||||
var fields = filter.fields;
|
||||
let fields = filter.fields;
|
||||
if (typeof fields === 'string') {
|
||||
// transform string into array containing this string
|
||||
filter.fields = fields = [fields];
|
||||
|
@ -387,14 +410,14 @@ Inclusion.include = function(objects, include, options, cb) {
|
|||
* @param callback
|
||||
*/
|
||||
function includeHasManyThrough(callback) {
|
||||
var sourceIds = [];
|
||||
const sourceIds = [];
|
||||
// Map for Indexing objects by their id for faster retrieval
|
||||
var objIdMap = {};
|
||||
for (var i = 0; i < objs.length; i++) {
|
||||
var obj = objs[i];
|
||||
const objIdMap = {};
|
||||
for (let i = 0; i < objs.length; i++) {
|
||||
const obj = objs[i];
|
||||
// one-to-many: foreign key reference is modelTo -> modelFrom.
|
||||
// use modelFrom.keyFrom in where filter later
|
||||
var sourceId = obj[relation.keyFrom];
|
||||
const sourceId = obj[relation.keyFrom];
|
||||
if (sourceId) {
|
||||
sourceIds.push(sourceId);
|
||||
objIdMap[sourceId.toString()] = obj;
|
||||
|
@ -405,7 +428,7 @@ Inclusion.include = function(objects, include, options, cb) {
|
|||
}
|
||||
// default filters are not applicable on through model. should be applied
|
||||
// on modelTo later in 2nd DB call.
|
||||
var throughFilter = {
|
||||
const throughFilter = {
|
||||
where: {},
|
||||
};
|
||||
throughFilter.where[relation.keyTo] = {
|
||||
|
@ -414,8 +437,9 @@ Inclusion.include = function(objects, include, options, cb) {
|
|||
if (polymorphic) {
|
||||
// handle polymorphic hasMany (reverse) in which case we need to filter
|
||||
// by discriminator to filter other types
|
||||
const throughModel = polymorphic.invert ? relation.modelTo : relation.modelFrom;
|
||||
throughFilter.where[polymorphic.discriminator] =
|
||||
relation.modelFrom.definition.name;
|
||||
throughModel.definition.name;
|
||||
}
|
||||
|
||||
// 1st DB Call of 2-step process. Get through model objects first
|
||||
|
@ -433,25 +457,25 @@ Inclusion.include = function(objects, include, options, cb) {
|
|||
return callback(err);
|
||||
}
|
||||
// start preparing for 2nd DB call.
|
||||
var targetIds = [];
|
||||
var targetObjsMap = {};
|
||||
for (var i = 0; i < throughObjs.length; i++) {
|
||||
var throughObj = throughObjs[i];
|
||||
var targetId = throughObj[relation.keyThrough];
|
||||
const targetIds = [];
|
||||
const targetObjsMap = {};
|
||||
for (let i = 0; i < throughObjs.length; i++) {
|
||||
const throughObj = throughObjs[i];
|
||||
const targetId = throughObj[relation.keyThrough];
|
||||
if (targetId) {
|
||||
// save targetIds for 2nd DB Call
|
||||
targetIds.push(targetId);
|
||||
var sourceObj = objIdMap[throughObj[relation.keyTo]];
|
||||
var targetIdStr = targetId.toString();
|
||||
const sourceObj = objIdMap[throughObj[relation.keyTo]];
|
||||
const targetIdStr = targetId.toString();
|
||||
// Since targetId can be duplicates, multiple source objs are put
|
||||
// into buckets.
|
||||
var objList = targetObjsMap[targetIdStr] =
|
||||
const objList = targetObjsMap[targetIdStr] =
|
||||
targetObjsMap[targetIdStr] || [];
|
||||
objList.push(sourceObj);
|
||||
}
|
||||
}
|
||||
// Polymorphic relation does not have idKey of modelTo. Find it manually
|
||||
var modelToIdName = idName(relation.modelTo);
|
||||
const modelToIdName = idName(relation.modelTo);
|
||||
filter.where[modelToIdName] = {
|
||||
inq: uniq(targetIds),
|
||||
};
|
||||
|
@ -472,7 +496,7 @@ Inclusion.include = function(objects, include, options, cb) {
|
|||
if (err) {
|
||||
return callback(err);
|
||||
}
|
||||
var tasks = [];
|
||||
const tasks = [];
|
||||
// simultaneously process subIncludes. Call it first as it is an async
|
||||
// process.
|
||||
if (subInclude && targets) {
|
||||
|
@ -485,13 +509,15 @@ Inclusion.include = function(objects, include, options, cb) {
|
|||
function targetLinkingTask(next) {
|
||||
async.each(targets, linkManyToMany, next);
|
||||
function linkManyToMany(target, next) {
|
||||
var targetId = target[modelToIdName];
|
||||
const targetId = target[modelToIdName];
|
||||
if (!targetId) {
|
||||
var err = new Error(g.f('LinkManyToMany received target that doesn\'t contain required "%s"',
|
||||
modelToIdName));
|
||||
const err = new Error(g.f(
|
||||
'LinkManyToMany received target that doesn\'t contain required "%s"',
|
||||
modelToIdName,
|
||||
));
|
||||
return next(err);
|
||||
}
|
||||
var objList = targetObjsMap[targetId.toString()];
|
||||
const objList = targetObjsMap[targetId.toString()];
|
||||
async.each(objList, function(obj, next) {
|
||||
if (!obj) return next();
|
||||
obj.__cachedRelations[relationName].push(target);
|
||||
|
@ -510,15 +536,15 @@ Inclusion.include = function(objects, include, options, cb) {
|
|||
* @param callback
|
||||
*/
|
||||
function includeReferencesMany(callback) {
|
||||
var modelToIdName = idName(relation.modelTo);
|
||||
var allTargetIds = [];
|
||||
const modelToIdName = idName(relation.modelTo);
|
||||
let allTargetIds = [];
|
||||
// Map for Indexing objects by their id for faster retrieval
|
||||
var targetObjsMap = {};
|
||||
for (var i = 0; i < objs.length; i++) {
|
||||
var obj = objs[i];
|
||||
const targetObjsMap = {};
|
||||
for (let i = 0; i < objs.length; i++) {
|
||||
const obj = objs[i];
|
||||
// one-to-many: foreign key reference is modelTo -> modelFrom.
|
||||
// use modelFrom.keyFrom in where filter later
|
||||
var targetIds = obj[relation.keyFrom];
|
||||
let targetIds = obj[relation.keyFrom];
|
||||
if (targetIds) {
|
||||
if (typeof targetIds === 'string') {
|
||||
// For relational DBs, the array is stored as stringified json
|
||||
|
@ -528,10 +554,10 @@ Inclusion.include = function(objects, include, options, cb) {
|
|||
// referencesMany has multiple targetIds per obj. We need to concat
|
||||
// them into allTargetIds before DB Call
|
||||
allTargetIds = allTargetIds.concat(targetIds);
|
||||
for (var j = 0; j < targetIds.length; j++) {
|
||||
var targetId = targetIds[j];
|
||||
var targetIdStr = targetId.toString();
|
||||
var objList = targetObjsMap[targetIdStr] =
|
||||
for (let j = 0; j < targetIds.length; j++) {
|
||||
const targetId = targetIds[j];
|
||||
const targetIdStr = targetId.toString();
|
||||
const objList = targetObjsMap[targetIdStr] =
|
||||
targetObjsMap[targetIdStr] || [];
|
||||
objList.push(obj);
|
||||
}
|
||||
|
@ -559,7 +585,7 @@ Inclusion.include = function(objects, include, options, cb) {
|
|||
if (err) {
|
||||
return callback(err);
|
||||
}
|
||||
var tasks = [];
|
||||
const tasks = [];
|
||||
// simultaneously process subIncludes
|
||||
if (subInclude && targets) {
|
||||
tasks.push(function subIncludesTask(next) {
|
||||
|
@ -572,7 +598,7 @@ Inclusion.include = function(objects, include, options, cb) {
|
|||
function targetLinkingTask(next) {
|
||||
async.each(targets, linkManyToMany, next);
|
||||
function linkManyToMany(target, next) {
|
||||
var objList = targetObjsMap[target[relation.keyTo].toString()];
|
||||
const objList = targetObjsMap[target[relation.keyTo].toString()];
|
||||
async.each(objList, function(obj, next) {
|
||||
if (!obj) return next();
|
||||
obj.__cachedRelations[relationName].push(target);
|
||||
|
@ -591,7 +617,7 @@ Inclusion.include = function(objects, include, options, cb) {
|
|||
*/
|
||||
function includeHasManySimple(callback) {
|
||||
// Map for Indexing objects by their id for faster retrieval
|
||||
var objIdMap2 = includeUtils.buildOneToOneIdentityMapWithOrigKeys(objs, relation.keyFrom);
|
||||
const objIdMap2 = includeUtils.buildOneToOneIdentityMapWithOrigKeys(objs, relation.keyFrom);
|
||||
|
||||
filter.where[relation.keyTo] = {
|
||||
inq: uniq(objIdMap2.getKeys()),
|
||||
|
@ -606,7 +632,7 @@ Inclusion.include = function(objects, include, options, cb) {
|
|||
if (err) {
|
||||
return callback(err);
|
||||
}
|
||||
var targetsIdMap = includeUtils.buildOneToManyIdentityMapWithOrigKeys(targets, relation.keyTo);
|
||||
const targetsIdMap = includeUtils.buildOneToManyIdentityMapWithOrigKeys(targets, relation.keyTo);
|
||||
includeUtils.join(objIdMap2, targetsIdMap, function(obj1, valueToMergeIn) {
|
||||
defineCachedRelations(obj1);
|
||||
obj1.__cachedRelations[relationName] = valueToMergeIn;
|
||||
|
@ -621,14 +647,14 @@ Inclusion.include = function(objects, include, options, cb) {
|
|||
* @param callback
|
||||
*/
|
||||
function includeHasMany(callback) {
|
||||
var sourceIds = [];
|
||||
const sourceIds = [];
|
||||
// Map for Indexing objects by their id for faster retrieval
|
||||
var objIdMap = {};
|
||||
for (var i = 0; i < objs.length; i++) {
|
||||
var obj = objs[i];
|
||||
const objIdMap = {};
|
||||
for (let i = 0; i < objs.length; i++) {
|
||||
const obj = objs[i];
|
||||
// one-to-many: foreign key reference is modelTo -> modelFrom.
|
||||
// use modelFrom.keyFrom in where filter later
|
||||
var sourceId = obj[relation.keyFrom];
|
||||
const sourceId = obj[relation.keyFrom];
|
||||
if (sourceId) {
|
||||
sourceIds.push(sourceId);
|
||||
objIdMap[sourceId.toString()] = obj;
|
||||
|
@ -656,7 +682,7 @@ Inclusion.include = function(objects, include, options, cb) {
|
|||
if (err) {
|
||||
return callback(err);
|
||||
}
|
||||
var tasks = [];
|
||||
const tasks = [];
|
||||
// simultaneously process subIncludes
|
||||
if (subInclude && targets) {
|
||||
tasks.push(function subIncludesTask(next) {
|
||||
|
@ -675,9 +701,9 @@ Inclusion.include = function(objects, include, options, cb) {
|
|||
async.each(targets, linkManyToOne, next);
|
||||
function linkManyToOne(target, next) {
|
||||
// fix for bug in hasMany with referencesMany
|
||||
var targetIds = [].concat(target[relation.keyTo]);
|
||||
const targetIds = [].concat(target[relation.keyTo]);
|
||||
async.each(targetIds, function(targetId, next) {
|
||||
var obj = objIdMap[targetId.toString()];
|
||||
const obj = objIdMap[targetId.toString()];
|
||||
if (!obj) return next();
|
||||
obj.__cachedRelations[relationName].push(target);
|
||||
processTargetObj(obj, next);
|
||||
|
@ -686,7 +712,7 @@ Inclusion.include = function(objects, include, options, cb) {
|
|||
return next(err);
|
||||
}
|
||||
|
||||
var objsWithEmptyRelation = objs.filter(function(obj) {
|
||||
const objsWithEmptyRelation = objs.filter(function(obj) {
|
||||
return obj.__cachedRelations[relationName].length === 0;
|
||||
});
|
||||
async.each(objsWithEmptyRelation, function(obj, next) {
|
||||
|
@ -707,22 +733,22 @@ Inclusion.include = function(objects, include, options, cb) {
|
|||
* @param callback
|
||||
*/
|
||||
function includePolymorphicBelongsTo(callback) {
|
||||
var targetIdsByType = {};
|
||||
const targetIdsByType = {};
|
||||
// Map for Indexing objects by their type and targetId for faster retrieval
|
||||
var targetObjMapByType = {};
|
||||
for (var i = 0; i < objs.length; i++) {
|
||||
var obj = objs[i];
|
||||
var discriminator = polymorphic.discriminator;
|
||||
var modelType = obj[discriminator];
|
||||
const targetObjMapByType = {};
|
||||
for (let i = 0; i < objs.length; i++) {
|
||||
const obj = objs[i];
|
||||
const discriminator = polymorphic.discriminator;
|
||||
const modelType = obj[discriminator];
|
||||
if (modelType) {
|
||||
targetIdsByType[modelType] = targetIdsByType[modelType] || [];
|
||||
targetObjMapByType[modelType] = targetObjMapByType[modelType] || {};
|
||||
var targetIds = targetIdsByType[modelType];
|
||||
var targetObjsMap = targetObjMapByType[modelType];
|
||||
var targetId = obj[relation.keyFrom];
|
||||
const targetIds = targetIdsByType[modelType];
|
||||
const targetObjsMap = targetObjMapByType[modelType];
|
||||
const targetId = obj[relation.keyFrom];
|
||||
if (targetId) {
|
||||
targetIds.push(targetId);
|
||||
var targetIdStr = targetId.toString();
|
||||
const targetIdStr = targetId.toString();
|
||||
targetObjsMap[targetIdStr] = targetObjsMap[targetIdStr] || [];
|
||||
// Is belongsTo. Multiple objects can have the same
|
||||
// targetId and therefore map value is an array
|
||||
|
@ -740,13 +766,13 @@ Inclusion.include = function(objects, include, options, cb) {
|
|||
* @param callback
|
||||
*/
|
||||
function processPolymorphicType(modelType, callback) {
|
||||
var typeFilter = {where: {}};
|
||||
const typeFilter = {where: {}};
|
||||
utils.mergeQuery(typeFilter, filter);
|
||||
var targetIds = targetIdsByType[modelType];
|
||||
const targetIds = targetIdsByType[modelType];
|
||||
typeFilter.where[relation.keyTo] = {
|
||||
inq: uniq(targetIds),
|
||||
};
|
||||
var Model = lookupModel(relation.modelFrom.dataSource.modelBuilder.
|
||||
const Model = lookupModel(relation.modelFrom.dataSource.modelBuilder.
|
||||
models, modelType);
|
||||
if (!Model) {
|
||||
callback(new Error(g.f('Discriminator type %s specified but no model exists with such name',
|
||||
|
@ -768,7 +794,7 @@ Inclusion.include = function(objects, include, options, cb) {
|
|||
if (err) {
|
||||
return callback(err);
|
||||
}
|
||||
var tasks = [];
|
||||
const tasks = [];
|
||||
|
||||
// simultaneously process subIncludes
|
||||
if (subInclude && targets) {
|
||||
|
@ -779,10 +805,10 @@ Inclusion.include = function(objects, include, options, cb) {
|
|||
// process each target object
|
||||
tasks.push(targetLinkingTask);
|
||||
function targetLinkingTask(next) {
|
||||
var targetObjsMap = targetObjMapByType[modelType];
|
||||
const targetObjsMap = targetObjMapByType[modelType];
|
||||
async.each(targets, linkOneToMany, next);
|
||||
function linkOneToMany(target, next) {
|
||||
var objList = targetObjsMap[target[relation.keyTo].toString()];
|
||||
const objList = targetObjsMap[target[relation.keyTo].toString()];
|
||||
async.each(objList, function(obj, next) {
|
||||
if (!obj) return next();
|
||||
obj.__cachedRelations[relationName] = target;
|
||||
|
@ -801,14 +827,14 @@ Inclusion.include = function(objects, include, options, cb) {
|
|||
* @param callback
|
||||
*/
|
||||
function includePolymorphicHasOne(callback) {
|
||||
var sourceIds = [];
|
||||
const sourceIds = [];
|
||||
// Map for Indexing objects by their id for faster retrieval
|
||||
var objIdMap = {};
|
||||
for (var i = 0; i < objs.length; i++) {
|
||||
var obj = objs[i];
|
||||
const objIdMap = {};
|
||||
for (let i = 0; i < objs.length; i++) {
|
||||
const obj = objs[i];
|
||||
// one-to-one: foreign key reference is modelTo -> modelFrom.
|
||||
// use modelFrom.keyFrom in where filter later
|
||||
var sourceId = obj[relation.keyFrom];
|
||||
const sourceId = obj[relation.keyFrom];
|
||||
if (sourceId) {
|
||||
sourceIds.push(sourceId);
|
||||
objIdMap[sourceId.toString()] = obj;
|
||||
|
@ -835,7 +861,7 @@ Inclusion.include = function(objects, include, options, cb) {
|
|||
if (err) {
|
||||
return callback(err);
|
||||
}
|
||||
var tasks = [];
|
||||
const tasks = [];
|
||||
// simultaneously process subIncludes
|
||||
if (subInclude && targets) {
|
||||
tasks.push(function subIncludesTask(next) {
|
||||
|
@ -847,9 +873,9 @@ Inclusion.include = function(objects, include, options, cb) {
|
|||
function targetLinkingTask(next) {
|
||||
async.each(targets, linkOneToOne, next);
|
||||
function linkOneToOne(target, next) {
|
||||
var sourceId = target[relation.keyTo];
|
||||
const sourceId = target[relation.keyTo];
|
||||
if (!sourceId) return next();
|
||||
var obj = objIdMap[sourceId.toString()];
|
||||
const obj = objIdMap[sourceId.toString()];
|
||||
if (!obj) return next();
|
||||
obj.__cachedRelations[relationName] = target;
|
||||
processTargetObj(obj, next);
|
||||
|
@ -865,24 +891,26 @@ Inclusion.include = function(objects, include, options, cb) {
|
|||
* @param callback
|
||||
*/
|
||||
function includeOneToOne(callback) {
|
||||
var targetIds = [];
|
||||
var objTargetIdMap = {};
|
||||
for (var i = 0; i < objs.length; i++) {
|
||||
var obj = objs[i];
|
||||
const targetIds = [];
|
||||
const objTargetIdMap = {};
|
||||
for (let i = 0; i < objs.length; i++) {
|
||||
const obj = objs[i];
|
||||
if (relation.type === 'belongsTo') {
|
||||
if (obj[relation.keyFrom] === null ||
|
||||
obj[relation.keyFrom] === undefined) {
|
||||
if (obj[relation.keyFrom] == null) {
|
||||
defineCachedRelations(obj);
|
||||
obj.__cachedRelations[relationName] = null;
|
||||
debug('ID property "%s" is missing in item %j', relation.keyFrom, obj);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
var targetId = obj[relation.keyFrom];
|
||||
const targetId = obj[relation.keyFrom];
|
||||
if (targetId) {
|
||||
targetIds.push(targetId);
|
||||
var targetIdStr = targetId.toString();
|
||||
const targetIdStr = targetId.toString();
|
||||
objTargetIdMap[targetIdStr] = objTargetIdMap[targetIdStr] || [];
|
||||
objTargetIdMap[targetIdStr].push(obj);
|
||||
} else {
|
||||
debug('ID property "%s" is missing in item %j', relation.keyFrom, obj);
|
||||
}
|
||||
defineCachedRelations(obj);
|
||||
obj.__cachedRelations[relationName] = null;
|
||||
|
@ -905,7 +933,7 @@ Inclusion.include = function(objects, include, options, cb) {
|
|||
if (err) {
|
||||
return callback(err);
|
||||
}
|
||||
var tasks = [];
|
||||
const tasks = [];
|
||||
// simultaneously process subIncludes
|
||||
if (subInclude && targets) {
|
||||
tasks.push(function subIncludesTask(next) {
|
||||
|
@ -917,8 +945,8 @@ Inclusion.include = function(objects, include, options, cb) {
|
|||
function targetLinkingTask(next) {
|
||||
async.each(targets, linkOneToMany, next);
|
||||
function linkOneToMany(target, next) {
|
||||
var targetId = target[relation.keyTo];
|
||||
var objList = objTargetIdMap[targetId.toString()];
|
||||
const targetId = target[relation.keyTo];
|
||||
const objList = objTargetIdMap[targetId.toString()];
|
||||
async.each(objList, function(obj, next) {
|
||||
if (!obj) return next();
|
||||
obj.__cachedRelations[relationName] = target;
|
||||
|
@ -953,7 +981,7 @@ Inclusion.include = function(objects, include, options, cb) {
|
|||
* @returns {*}
|
||||
*/
|
||||
function processTargetObj(obj, callback) {
|
||||
var isInst = obj instanceof self;
|
||||
const isInst = obj instanceof self;
|
||||
|
||||
// Calling the relation method on the instance
|
||||
if (relation.type === 'belongsTo') {
|
||||
|
@ -995,21 +1023,21 @@ Inclusion.include = function(objects, include, options, cb) {
|
|||
callback);
|
||||
}
|
||||
|
||||
var inst = (obj instanceof self) ? obj : new self(obj);
|
||||
const inst = (obj instanceof self) ? obj : new self(obj);
|
||||
|
||||
// If related objects are not cached by include Handlers, directly call
|
||||
// related accessor function even though it is not very efficient
|
||||
var related; // relation accessor function
|
||||
let related; // relation accessor function
|
||||
|
||||
if ((relation.multiple || relation.type === 'belongsTo') && scope) {
|
||||
var includeScope = {};
|
||||
var filter = scope.conditions();
|
||||
const includeScope = {};
|
||||
const filter = scope.conditions();
|
||||
|
||||
// make sure not to miss any fields for sub includes
|
||||
if (filter.fields && Array.isArray(subInclude) && relation.modelTo.relations) {
|
||||
includeScope.fields = [];
|
||||
subInclude.forEach(function(name) {
|
||||
var rel = relation.modelTo.relations[name];
|
||||
const rel = relation.modelTo.relations[name];
|
||||
if (rel && rel.type === 'belongsTo') {
|
||||
includeScope.fields.push(rel.keyFrom);
|
||||
}
|
||||
|
|
|
@ -1,15 +1,31 @@
|
|||
// Copyright IBM Corp. 2015,2016. All Rights Reserved.
|
||||
// Copyright IBM Corp. 2015,2019. All Rights Reserved.
|
||||
// Node module: loopback-datasource-juggler
|
||||
// This file is licensed under the MIT License.
|
||||
// License text available at https://opensource.org/licenses/MIT
|
||||
|
||||
'use strict';
|
||||
|
||||
const g = require('strong-globalize')();
|
||||
|
||||
module.exports.buildOneToOneIdentityMapWithOrigKeys = buildOneToOneIdentityMapWithOrigKeys;
|
||||
module.exports.buildOneToManyIdentityMapWithOrigKeys = buildOneToManyIdentityMapWithOrigKeys;
|
||||
module.exports.join = join;
|
||||
module.exports.KVMap = KVMap;
|
||||
|
||||
const util = require('util');
|
||||
|
||||
function getId(obj, idName) {
|
||||
const id = obj && obj[idName];
|
||||
if (id == null) {
|
||||
const msg = g.f('ID property "%s" is missing for included item: %j. ' +
|
||||
'Please make sure `fields` include "%s" if it\'s present in the `filter`',
|
||||
idName, obj, idName);
|
||||
const err = new Error(msg);
|
||||
err.statusCode = 400;
|
||||
throw err;
|
||||
}
|
||||
return id;
|
||||
}
|
||||
/**
|
||||
* Effectively builds associative map on id -> object relation and stores original keys.
|
||||
* Map returned in form of object with ids in keys and object as values.
|
||||
|
@ -19,21 +35,21 @@ module.exports.KVMap = KVMap;
|
|||
* @returns {} object where keys are ids and values are objects itself
|
||||
*/
|
||||
function buildOneToOneIdentityMapWithOrigKeys(objs, idName) {
|
||||
var kvMap = new KVMap();
|
||||
for (var i = 0; i < objs.length; i++) {
|
||||
var obj = objs[i];
|
||||
var id = obj[idName];
|
||||
const kvMap = new KVMap();
|
||||
for (let i = 0; i < objs.length; i++) {
|
||||
const obj = objs[i];
|
||||
const id = getId(obj, idName);
|
||||
kvMap.set(id, obj);
|
||||
}
|
||||
return kvMap;
|
||||
}
|
||||
|
||||
function buildOneToManyIdentityMapWithOrigKeys(objs, idName) {
|
||||
var kvMap = new KVMap();
|
||||
for (var i = 0; i < objs.length; i++) {
|
||||
var obj = objs[i];
|
||||
var id = obj[idName];
|
||||
var value = kvMap.get(id) || [];
|
||||
const kvMap = new KVMap();
|
||||
for (let i = 0; i < objs.length; i++) {
|
||||
const obj = objs[i];
|
||||
const id = getId(obj, idName);
|
||||
const value = kvMap.get(id) || [];
|
||||
value.push(obj);
|
||||
kvMap.set(id, value);
|
||||
}
|
||||
|
@ -48,11 +64,11 @@ function buildOneToManyIdentityMapWithOrigKeys(objs, idName) {
|
|||
* @param mergeF function(obj, objectsToMergeIn)
|
||||
*/
|
||||
function join(oneToOneIdMap, oneToManyIdMap, mergeF) {
|
||||
var ids = oneToOneIdMap.getKeys();
|
||||
for (var i = 0; i < ids.length; i++) {
|
||||
var id = ids[i];
|
||||
var obj = oneToOneIdMap.get(id);
|
||||
var objectsToMergeIn = oneToManyIdMap.get(id) || [];
|
||||
const ids = oneToOneIdMap.getKeys();
|
||||
for (let i = 0; i < ids.length; i++) {
|
||||
const id = ids[i];
|
||||
const obj = oneToOneIdMap.get(id);
|
||||
const objectsToMergeIn = oneToManyIdMap.get(id) || [];
|
||||
mergeF(obj, objectsToMergeIn);
|
||||
}
|
||||
}
|
||||
|
@ -63,20 +79,20 @@ function join(oneToOneIdMap, oneToManyIdMap, mergeF) {
|
|||
* @constructor
|
||||
*/
|
||||
function KVMap() {
|
||||
var _originalKeyFieldName = 'originalKey';
|
||||
var _valueKeyFieldName = 'value';
|
||||
var _dict = {};
|
||||
var keyToString = function(key) { return key.toString(); };
|
||||
var mapImpl = {
|
||||
const _originalKeyFieldName = 'originalKey';
|
||||
const _valueKeyFieldName = 'value';
|
||||
const _dict = {};
|
||||
const keyToString = function(key) { return key.toString(); };
|
||||
const mapImpl = {
|
||||
set: function(key, value) {
|
||||
var recordObj = {};
|
||||
const recordObj = {};
|
||||
recordObj[_originalKeyFieldName] = key;
|
||||
recordObj[_valueKeyFieldName] = value;
|
||||
_dict[keyToString(key)] = recordObj;
|
||||
return true;
|
||||
},
|
||||
get: function(key) {
|
||||
var storeObj = _dict[keyToString(key)];
|
||||
const storeObj = _dict[keyToString(key)];
|
||||
if (storeObj) {
|
||||
return storeObj[_valueKeyFieldName];
|
||||
} else {
|
||||
|
@ -88,12 +104,12 @@ function KVMap() {
|
|||
return true;
|
||||
},
|
||||
exist: function(key) {
|
||||
var result = _dict.hasOwnProperty(keyToString(key));
|
||||
const result = _dict.hasOwnProperty(keyToString(key));
|
||||
return result;
|
||||
},
|
||||
getKeys: function() {
|
||||
var result = [];
|
||||
for (var key in _dict) {
|
||||
const result = [];
|
||||
for (const key in _dict) {
|
||||
result.push(_dict[key][_originalKeyFieldName]);
|
||||
}
|
||||
return result;
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// Copyright IBM Corp. 2013,2016. All Rights Reserved.
|
||||
// Copyright IBM Corp. 2013,2019. All Rights Reserved.
|
||||
// Node module: loopback-datasource-juggler
|
||||
// This file is licensed under the MIT License.
|
||||
// License text available at https://opensource.org/licenses/MIT
|
||||
|
@ -13,14 +13,14 @@ module.exports = function getIntrospector(ModelBuilder) {
|
|||
}
|
||||
|
||||
// Check registered schemaTypes
|
||||
for (var t in ModelBuilder.schemaTypes) {
|
||||
var st = ModelBuilder.schemaTypes[t];
|
||||
for (const t in ModelBuilder.schemaTypes) {
|
||||
const st = ModelBuilder.schemaTypes[t];
|
||||
if (st !== Object && st !== Array && (value instanceof st)) {
|
||||
return t;
|
||||
}
|
||||
}
|
||||
|
||||
var type = typeof value;
|
||||
const type = typeof value;
|
||||
if (type === 'string' || type === 'number' || type === 'boolean') {
|
||||
return type;
|
||||
}
|
||||
|
@ -29,9 +29,9 @@ module.exports = function getIntrospector(ModelBuilder) {
|
|||
return 'date';
|
||||
}
|
||||
|
||||
var itemType;
|
||||
let itemType;
|
||||
if (Array.isArray(value)) {
|
||||
for (var i = 0; i < value.length; i++) {
|
||||
for (let i = 0; i < value.length; i++) {
|
||||
if (value[i] === null || value[i] === undefined) {
|
||||
continue;
|
||||
}
|
||||
|
@ -47,8 +47,8 @@ module.exports = function getIntrospector(ModelBuilder) {
|
|||
return value.constructor.name;
|
||||
}
|
||||
|
||||
var properties = {};
|
||||
for (var p in value) {
|
||||
const properties = {};
|
||||
for (const p in value) {
|
||||
itemType = introspectType(value[p]);
|
||||
if (itemType) {
|
||||
properties[p] = itemType;
|
||||
|
|
24
lib/jutil.js
24
lib/jutil.js
|
@ -1,11 +1,11 @@
|
|||
// Copyright IBM Corp. 2011,2016. All Rights Reserved.
|
||||
// Copyright IBM Corp. 2011,2019. All Rights Reserved.
|
||||
// Node module: loopback-datasource-juggler
|
||||
// This file is licensed under the MIT License.
|
||||
// License text available at https://opensource.org/licenses/MIT
|
||||
|
||||
'use strict';
|
||||
|
||||
var util = require('util');
|
||||
const util = require('util');
|
||||
|
||||
/**
|
||||
*
|
||||
|
@ -24,7 +24,7 @@ exports.inherits = function(newClass, baseClass, options) {
|
|||
Object.keys(baseClass).forEach(function(classProp) {
|
||||
if (classProp !== 'super_' && (!newClass.hasOwnProperty(classProp) ||
|
||||
options.override)) {
|
||||
var pd = Object.getOwnPropertyDescriptor(baseClass, classProp);
|
||||
const pd = Object.getOwnPropertyDescriptor(baseClass, classProp);
|
||||
Object.defineProperty(newClass, classProp, pd);
|
||||
}
|
||||
});
|
||||
|
@ -75,13 +75,13 @@ exports.mixin = function(newClass, mixinClass, options) {
|
|||
|
||||
function mixInto(sourceScope, targetScope, options) {
|
||||
Object.keys(sourceScope).forEach(function(propertyName) {
|
||||
var targetPropertyExists = targetScope.hasOwnProperty(propertyName);
|
||||
var sourceProperty = Object.getOwnPropertyDescriptor(sourceScope, propertyName);
|
||||
var targetProperty = targetPropertyExists && Object.getOwnPropertyDescriptor(targetScope, propertyName);
|
||||
var sourceIsFunc = typeof sourceProperty.value === 'function';
|
||||
var isFunc = targetPropertyExists && typeof targetProperty.value === 'function';
|
||||
var isDelegate = isFunc && targetProperty.value._delegate;
|
||||
var shouldOverride = options.override || !targetPropertyExists || isDelegate;
|
||||
const targetPropertyExists = targetScope.hasOwnProperty(propertyName);
|
||||
const sourceProperty = Object.getOwnPropertyDescriptor(sourceScope, propertyName);
|
||||
const targetProperty = targetPropertyExists && Object.getOwnPropertyDescriptor(targetScope, propertyName);
|
||||
const sourceIsFunc = typeof sourceProperty.value === 'function';
|
||||
const isFunc = targetPropertyExists && typeof targetProperty.value === 'function';
|
||||
const isDelegate = isFunc && targetProperty.value._delegate;
|
||||
const shouldOverride = options.override || !targetPropertyExists || isDelegate;
|
||||
|
||||
if (propertyName == '_mixins') {
|
||||
mergeMixins(sourceScope._mixins, targetScope._mixins);
|
||||
|
@ -96,8 +96,8 @@ function mixInto(sourceScope, targetScope, options) {
|
|||
|
||||
function mergeMixins(source, target) {
|
||||
// hand-written equivalent of lodash.union()
|
||||
for (var ix in source) {
|
||||
var mx = source[ix];
|
||||
for (const ix in source) {
|
||||
const mx = source[ix];
|
||||
if (target.indexOf(mx) === -1)
|
||||
target.push(mx);
|
||||
}
|
||||
|
|
|
@ -1,9 +1,14 @@
|
|||
// Copyright IBM Corp. 2017,2019. All Rights Reserved.
|
||||
// Node module: loopback-datasource-juggler
|
||||
// This file is licensed under the MIT License.
|
||||
// License text available at https://opensource.org/licenses/MIT
|
||||
|
||||
'use strict';
|
||||
|
||||
var assert = require('assert');
|
||||
var async = require('async');
|
||||
var debug = require('debug')('loopback:kvao:delete-all');
|
||||
var utils = require('../utils');
|
||||
const assert = require('assert');
|
||||
const async = require('async');
|
||||
const debug = require('debug')('loopback:kvao:delete-all');
|
||||
const utils = require('../utils');
|
||||
|
||||
/**
|
||||
* Delete all keys (and values) associated to the current model.
|
||||
|
@ -27,17 +32,17 @@ module.exports = function deleteAll(options, callback) {
|
|||
|
||||
callback = callback || utils.createPromiseCallback();
|
||||
|
||||
var connector = this.getConnector();
|
||||
const connector = this.getConnector();
|
||||
if (typeof connector.deleteAll === 'function') {
|
||||
connector.deleteAll(this.modelName, options, callback);
|
||||
} else if (typeof connector.delete === 'function') {
|
||||
debug('Falling back to unoptimized key-value pair deletion');
|
||||
iterateAndDelete(connector, this.modelName, options, callback);
|
||||
} else {
|
||||
var errMsg = 'Connector does not support key-value pair deletion';
|
||||
const errMsg = 'Connector does not support key-value pair deletion';
|
||||
debug(errMsg);
|
||||
process.nextTick(function() {
|
||||
var err = new Error(errMsg);
|
||||
const err = new Error(errMsg);
|
||||
err.statusCode = 501;
|
||||
callback(err);
|
||||
});
|
||||
|
@ -46,8 +51,8 @@ module.exports = function deleteAll(options, callback) {
|
|||
};
|
||||
|
||||
function iterateAndDelete(connector, modelName, options, callback) {
|
||||
var iter = connector.iterateKeys(modelName, {});
|
||||
var keys = [];
|
||||
const iter = connector.iterateKeys(modelName, {});
|
||||
const keys = [];
|
||||
iter.next(onNextKey);
|
||||
|
||||
function onNextKey(err, key) {
|
||||
|
|
|
@ -1,8 +1,13 @@
|
|||
// Copyright IBM Corp. 2017,2019. All Rights Reserved.
|
||||
// Node module: loopback-datasource-juggler
|
||||
// This file is licensed under the MIT License.
|
||||
// License text available at https://opensource.org/licenses/MIT
|
||||
|
||||
'use strict';
|
||||
|
||||
var assert = require('assert');
|
||||
var debug = require('debug')('loopback:kvao:delete');
|
||||
var utils = require('../utils');
|
||||
const assert = require('assert');
|
||||
const debug = require('debug')('loopback:kvao:delete');
|
||||
const utils = require('../utils');
|
||||
|
||||
/**
|
||||
* Delete the key-value pair associated to the given key.
|
||||
|
@ -28,14 +33,14 @@ module.exports = function keyValueDelete(key, options, callback) {
|
|||
|
||||
callback = callback || utils.createPromiseCallback();
|
||||
|
||||
var connector = this.getConnector();
|
||||
const connector = this.getConnector();
|
||||
if (typeof connector.delete === 'function') {
|
||||
connector.delete(this.modelName, key, options, callback);
|
||||
} else {
|
||||
var errMsg = 'Connector does not support key-value pair deletion';
|
||||
const errMsg = 'Connector does not support key-value pair deletion';
|
||||
debug(errMsg);
|
||||
process.nextTick(function() {
|
||||
var err = new Error(errMsg);
|
||||
const err = new Error(errMsg);
|
||||
err.statusCode = 501;
|
||||
callback(err);
|
||||
});
|
||||
|
|
|
@ -1,7 +1,12 @@
|
|||
// Copyright IBM Corp. 2016,2019. All Rights Reserved.
|
||||
// Node module: loopback-datasource-juggler
|
||||
// This file is licensed under the MIT License.
|
||||
// License text available at https://opensource.org/licenses/MIT
|
||||
|
||||
'use strict';
|
||||
|
||||
var assert = require('assert');
|
||||
var utils = require('../utils');
|
||||
const assert = require('assert');
|
||||
const utils = require('../utils');
|
||||
|
||||
/**
|
||||
* Set the TTL (time to live) in ms (milliseconds) for a given key. TTL is the
|
||||
|
|
|
@ -1,7 +1,12 @@
|
|||
// Copyright IBM Corp. 2016,2019. All Rights Reserved.
|
||||
// Node module: loopback-datasource-juggler
|
||||
// This file is licensed under the MIT License.
|
||||
// License text available at https://opensource.org/licenses/MIT
|
||||
|
||||
'use strict';
|
||||
|
||||
var assert = require('assert');
|
||||
var utils = require('../utils');
|
||||
const assert = require('assert');
|
||||
const utils = require('../utils');
|
||||
|
||||
/**
|
||||
* Return the value associated with a given key.
|
||||
|
|
|
@ -1,7 +1,12 @@
|
|||
// Copyright IBM Corp. 2016,2019. All Rights Reserved.
|
||||
// Node module: loopback-datasource-juggler
|
||||
// This file is licensed under the MIT License.
|
||||
// License text available at https://opensource.org/licenses/MIT
|
||||
|
||||
'use strict';
|
||||
|
||||
function KeyValueAccessObject() {
|
||||
};
|
||||
}
|
||||
|
||||
module.exports = KeyValueAccessObject;
|
||||
|
||||
|
|
|
@ -1,7 +1,12 @@
|
|||
// Copyright IBM Corp. 2016,2019. All Rights Reserved.
|
||||
// Node module: loopback-datasource-juggler
|
||||
// This file is licensed under the MIT License.
|
||||
// License text available at https://opensource.org/licenses/MIT
|
||||
|
||||
'use strict';
|
||||
|
||||
var assert = require('assert');
|
||||
var utils = require('../utils');
|
||||
const assert = require('assert');
|
||||
const utils = require('../utils');
|
||||
|
||||
/**
|
||||
* Asynchronously iterate all keys in the database. Similar to `.keys()` but
|
||||
|
@ -26,7 +31,7 @@ module.exports = function keyValueIterateKeys(filter, options) {
|
|||
assert(typeof filter === 'object', 'filter must be an object');
|
||||
assert(typeof options === 'object', 'options must be an object');
|
||||
|
||||
var iter = this.getConnector().iterateKeys(this.modelName, filter, options);
|
||||
const iter = this.getConnector().iterateKeys(this.modelName, filter, options);
|
||||
// promisify the returned iterator
|
||||
return {
|
||||
next: function(callback) {
|
||||
|
|
|
@ -1,7 +1,12 @@
|
|||
// Copyright IBM Corp. 2016,2019. All Rights Reserved.
|
||||
// Node module: loopback-datasource-juggler
|
||||
// This file is licensed under the MIT License.
|
||||
// License text available at https://opensource.org/licenses/MIT
|
||||
|
||||
'use strict';
|
||||
|
||||
var assert = require('assert');
|
||||
var utils = require('../utils');
|
||||
const assert = require('assert');
|
||||
const utils = require('../utils');
|
||||
|
||||
/**
|
||||
* Return all keys in the database.
|
||||
|
@ -41,8 +46,8 @@ module.exports = function keyValueKeys(filter, options, callback) {
|
|||
|
||||
callback = callback || utils.createPromiseCallback();
|
||||
|
||||
var iter = this.iterateKeys(filter, options);
|
||||
var keys = [];
|
||||
const iter = this.iterateKeys(filter, options);
|
||||
const keys = [];
|
||||
iter.next(onNextKey);
|
||||
|
||||
function onNextKey(err, key) {
|
||||
|
|
|
@ -1,7 +1,12 @@
|
|||
// Copyright IBM Corp. 2016,2019. All Rights Reserved.
|
||||
// Node module: loopback-datasource-juggler
|
||||
// This file is licensed under the MIT License.
|
||||
// License text available at https://opensource.org/licenses/MIT
|
||||
|
||||
'use strict';
|
||||
|
||||
var assert = require('assert');
|
||||
var utils = require('../utils');
|
||||
const assert = require('assert');
|
||||
const utils = require('../utils');
|
||||
|
||||
/**
|
||||
* Persist a value and associate it with the given key.
|
||||
|
|
|
@ -1,7 +1,12 @@
|
|||
// Copyright IBM Corp. 2016,2019. All Rights Reserved.
|
||||
// Node module: loopback-datasource-juggler
|
||||
// This file is licensed under the MIT License.
|
||||
// License text available at https://opensource.org/licenses/MIT
|
||||
|
||||
'use strict';
|
||||
|
||||
var assert = require('assert');
|
||||
var utils = require('../utils');
|
||||
const assert = require('assert');
|
||||
const utils = require('../utils');
|
||||
|
||||
/**
|
||||
* Return the TTL (time to live) for a given key. TTL is the remaining time
|
||||
|
|
67
lib/list.js
67
lib/list.js
|
@ -1,18 +1,21 @@
|
|||
// Copyright IBM Corp. 2012,2016. All Rights Reserved.
|
||||
// Copyright IBM Corp. 2012,2019. All Rights Reserved.
|
||||
// Node module: loopback-datasource-juggler
|
||||
// This file is licensed under the MIT License.
|
||||
// License text available at https://opensource.org/licenses/MIT
|
||||
|
||||
'use strict';
|
||||
|
||||
var g = require('strong-globalize')();
|
||||
var util = require('util');
|
||||
var Any = require('./types').Types.Any;
|
||||
const g = require('strong-globalize')();
|
||||
const util = require('util');
|
||||
const Any = require('./types').Types.Any;
|
||||
const {
|
||||
applyParentProperty,
|
||||
} = require('./utils');
|
||||
|
||||
module.exports = List;
|
||||
|
||||
function List(items, itemType, parent) {
|
||||
var list = this;
|
||||
const list = this;
|
||||
if (!(list instanceof List)) {
|
||||
return new List(items, itemType, parent);
|
||||
}
|
||||
|
@ -27,7 +30,12 @@ function List(items, itemType, parent) {
|
|||
}
|
||||
}
|
||||
|
||||
var arr = [];
|
||||
if (typeof items === 'number') {
|
||||
// trying to initialise empty array with a length
|
||||
items = [...new Array(items)];
|
||||
}
|
||||
|
||||
const arr = [];
|
||||
arr.__proto__ = List.prototype;
|
||||
|
||||
items = items || [];
|
||||
|
@ -56,6 +64,7 @@ function List(items, itemType, parent) {
|
|||
});
|
||||
|
||||
if (parent) {
|
||||
// List constructor now called with actual model instance
|
||||
Object.defineProperty(arr, 'parent', {
|
||||
writable: true,
|
||||
enumerable: false,
|
||||
|
@ -65,10 +74,11 @@ function List(items, itemType, parent) {
|
|||
|
||||
items.forEach(function(item, i) {
|
||||
if (itemType && !(item instanceof itemType)) {
|
||||
arr[i] = itemType(item);
|
||||
arr[i] = arr.toItem(item);
|
||||
} else {
|
||||
arr[i] = item;
|
||||
}
|
||||
if (parent && arr[i] && typeof arr[i] === 'object') applyParentProperty(arr[i], parent);
|
||||
});
|
||||
|
||||
return arr;
|
||||
|
@ -76,18 +86,37 @@ function List(items, itemType, parent) {
|
|||
|
||||
util.inherits(List, Array);
|
||||
|
||||
var _push = List.prototype.push;
|
||||
const _push = List.prototype.push;
|
||||
|
||||
List.prototype.toItem = function(item) {
|
||||
if (isClass(this.itemType)) {
|
||||
return new this.itemType(item);
|
||||
} else {
|
||||
if (Array.isArray(item)) {
|
||||
return item;
|
||||
} else if (this.itemType === Date) {
|
||||
if (item === null) return null;
|
||||
return new Date(item);
|
||||
} else {
|
||||
return this.itemType(item);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
List.prototype.push = function(obj) {
|
||||
var item = this.itemType && (obj instanceof this.itemType) ? obj : this.itemType(obj);
|
||||
const item = this.itemType && (obj instanceof this.itemType) ? obj : this.toItem(obj);
|
||||
if (item && typeof item === 'object' && this.parent) applyParentProperty(item, this.parent);
|
||||
_push.call(this, item);
|
||||
return item;
|
||||
};
|
||||
|
||||
List.prototype.toObject = function(onlySchema, removeHidden, removeProtected) {
|
||||
var items = [];
|
||||
const items = [];
|
||||
this.forEach(function(item) {
|
||||
if (item && typeof item === 'object' && item.toObject) {
|
||||
if (item && Array.isArray(item) && item.toArray) {
|
||||
const subArray = item.toArray();
|
||||
items.push(subArray);
|
||||
} else if (item && typeof item === 'object' && item.toObject) {
|
||||
items.push(item.toObject(onlySchema, removeHidden, removeProtected));
|
||||
} else {
|
||||
items.push(item);
|
||||
|
@ -96,6 +125,19 @@ List.prototype.toObject = function(onlySchema, removeHidden, removeProtected) {
|
|||
return items;
|
||||
};
|
||||
|
||||
/**
|
||||
* Convert itself to a plain array.
|
||||
*
|
||||
* Some modules such as `should` checks prototype for comparison
|
||||
*/
|
||||
List.prototype.toArray = function() {
|
||||
const items = [];
|
||||
this.forEach(function(item) {
|
||||
items.push(item);
|
||||
});
|
||||
return items;
|
||||
};
|
||||
|
||||
List.prototype.toJSON = function() {
|
||||
return this.toObject(true);
|
||||
};
|
||||
|
@ -104,3 +146,6 @@ List.prototype.toString = function() {
|
|||
return JSON.stringify(this.toJSON());
|
||||
};
|
||||
|
||||
function isClass(fn) {
|
||||
return fn && fn.toString().indexOf('class ') === 0;
|
||||
}
|
||||
|
|
|
@ -1,12 +1,13 @@
|
|||
// Copyright IBM Corp. 2014,2016. All Rights Reserved.
|
||||
// Copyright IBM Corp. 2014,2019. All Rights Reserved.
|
||||
// Node module: loopback-datasource-juggler
|
||||
// This file is licensed under the MIT License.
|
||||
// License text available at https://opensource.org/licenses/MIT
|
||||
|
||||
'use strict';
|
||||
|
||||
var debug = require('debug')('loopback:mixin');
|
||||
var assert = require('assert');
|
||||
var DefaultModelBaseClass = require('./model.js');
|
||||
const debug = require('debug')('loopback:mixin');
|
||||
const assert = require('assert');
|
||||
const DefaultModelBaseClass = require('./model.js');
|
||||
|
||||
function isModelClass(cls) {
|
||||
if (!cls) {
|
||||
|
@ -29,7 +30,7 @@ function MixinProvider(modelBuilder) {
|
|||
* @param {Object} options
|
||||
*/
|
||||
MixinProvider.prototype.applyMixin = function applyMixin(modelClass, name, options) {
|
||||
var fn = this.mixins[name];
|
||||
const fn = this.mixins[name];
|
||||
if (typeof fn === 'function') {
|
||||
if (modelClass.dataSource) {
|
||||
fn(modelClass, options || {});
|
||||
|
@ -40,12 +41,12 @@ MixinProvider.prototype.applyMixin = function applyMixin(modelClass, name, optio
|
|||
}
|
||||
} else {
|
||||
// Try model name
|
||||
var model = this.modelBuilder.getModel(name);
|
||||
const model = this.modelBuilder.getModel(name);
|
||||
if (model) {
|
||||
debug('Mixin is resolved to a model: %s', name);
|
||||
modelClass.mixin(model, options);
|
||||
} else {
|
||||
var errMsg = 'Model "' + modelClass.modelName + '" uses unknown mixin: ' + name;
|
||||
const errMsg = 'Model "' + modelClass.modelName + '" uses unknown mixin: ' + name;
|
||||
debug(errMsg);
|
||||
throw new Error(errMsg);
|
||||
}
|
||||
|
|
|
@ -1,31 +1,36 @@
|
|||
// Copyright IBM Corp. 2013,2016. All Rights Reserved.
|
||||
// Copyright IBM Corp. 2013,2019. All Rights Reserved.
|
||||
// Node module: loopback-datasource-juggler
|
||||
// This file is licensed under the MIT License.
|
||||
// License text available at https://opensource.org/licenses/MIT
|
||||
|
||||
'use strict';
|
||||
|
||||
/*!
|
||||
* Module dependencies
|
||||
*/
|
||||
|
||||
var g = require('strong-globalize')();
|
||||
var inflection = require('inflection');
|
||||
var EventEmitter = require('events').EventEmitter;
|
||||
var util = require('util');
|
||||
var assert = require('assert');
|
||||
var deprecated = require('depd')('loopback-datasource-juggler');
|
||||
var DefaultModelBaseClass = require('./model.js');
|
||||
var List = require('./list.js');
|
||||
var ModelDefinition = require('./model-definition.js');
|
||||
var deepMerge = require('./utils').deepMerge;
|
||||
var deepMergeProperty = require('./utils').deepMergeProperty;
|
||||
var rankArrayElements = require('./utils').rankArrayElements;
|
||||
var MixinProvider = require('./mixins');
|
||||
const g = require('strong-globalize')();
|
||||
const inflection = require('inflection');
|
||||
const EventEmitter = require('events').EventEmitter;
|
||||
const util = require('util');
|
||||
const assert = require('assert');
|
||||
const deprecated = require('depd')('loopback-datasource-juggler');
|
||||
const DefaultModelBaseClass = require('./model.js');
|
||||
const List = require('./list.js');
|
||||
const ModelDefinition = require('./model-definition.js');
|
||||
const MixinProvider = require('./mixins');
|
||||
const {
|
||||
deepMerge,
|
||||
deepMergeProperty,
|
||||
rankArrayElements,
|
||||
isClass,
|
||||
applyParentProperty,
|
||||
} = require('./utils');
|
||||
|
||||
// Set up types
|
||||
require('./types')(ModelBuilder);
|
||||
|
||||
var introspect = require('./introspection')(ModelBuilder);
|
||||
const introspect = require('./introspection')(ModelBuilder);
|
||||
|
||||
/*!
|
||||
* Export public API
|
||||
|
@ -35,7 +40,7 @@ exports.ModelBuilder = exports.Schema = ModelBuilder;
|
|||
/*!
|
||||
* Helpers
|
||||
*/
|
||||
var slice = Array.prototype.slice;
|
||||
const slice = Array.prototype.slice;
|
||||
|
||||
/**
|
||||
* ModelBuilder - A builder to define data models.
|
||||
|
@ -74,7 +79,7 @@ function isModelClass(cls) {
|
|||
* @returns {ModelClass} The model class
|
||||
*/
|
||||
ModelBuilder.prototype.getModel = function(name, forceCreate) {
|
||||
var model = this.models[name];
|
||||
let model = this.models[name];
|
||||
if (!model && forceCreate) {
|
||||
model = this.define(name, {}, {unresolved: true});
|
||||
}
|
||||
|
@ -120,13 +125,13 @@ ModelBuilder.prototype.getModelDefinition = function(name) {
|
|||
*
|
||||
*/
|
||||
ModelBuilder.prototype.define = function defineClass(className, properties, settings, parent) {
|
||||
var modelBuilder = this;
|
||||
var args = slice.call(arguments);
|
||||
var pluralName = (settings && settings.plural) ||
|
||||
const modelBuilder = this;
|
||||
const args = slice.call(arguments);
|
||||
const pluralName = (settings && settings.plural) ||
|
||||
inflection.pluralize(className);
|
||||
|
||||
var httpOptions = (settings && settings.http) || {};
|
||||
var pathName = httpOptions.path || pluralName;
|
||||
const httpOptions = (settings && settings.http) || {};
|
||||
let pathName = httpOptions.path || pluralName;
|
||||
|
||||
if (!className) {
|
||||
throw new Error(g.f('Class name required'));
|
||||
|
@ -149,8 +154,8 @@ ModelBuilder.prototype.define = function defineClass(className, properties, sett
|
|||
}
|
||||
|
||||
// Set up the base model class
|
||||
var ModelBaseClass = parent || this.defaultModelBaseClass;
|
||||
var baseClass = settings.base || settings['super'];
|
||||
let ModelBaseClass = parent || this.defaultModelBaseClass;
|
||||
const baseClass = settings.base || settings['super'];
|
||||
if (baseClass) {
|
||||
// Normalize base model property
|
||||
settings.base = baseClass;
|
||||
|
@ -180,7 +185,7 @@ ModelBuilder.prototype.define = function defineClass(className, properties, sett
|
|||
}
|
||||
|
||||
// Check if there is a unresolved model with the same name
|
||||
var ModelClass = this.models[className];
|
||||
let ModelClass = this.models[className];
|
||||
|
||||
// Create the ModelClass if it doesn't exist or it's resolved (override)
|
||||
// TODO: [rfeng] We need to decide what names to use for built-in models such as User.
|
||||
|
@ -188,11 +193,11 @@ ModelBuilder.prototype.define = function defineClass(className, properties, sett
|
|||
ModelClass = createModelClassCtor(className, ModelBaseClass);
|
||||
|
||||
// mix in EventEmitter (don't inherit from)
|
||||
var events = new EventEmitter();
|
||||
const events = new EventEmitter();
|
||||
// The model can have more than 10 listeners for lazy relationship setup
|
||||
// See https://github.com/strongloop/loopback/issues/404
|
||||
events.setMaxListeners(32);
|
||||
for (var f in EventEmitter.prototype) {
|
||||
for (const f in EventEmitter.prototype) {
|
||||
if (typeof EventEmitter.prototype[f] === 'function') {
|
||||
ModelClass[f] = EventEmitter.prototype[f].bind(events);
|
||||
}
|
||||
|
@ -229,7 +234,7 @@ ModelBuilder.prototype.define = function defineClass(className, properties, sett
|
|||
hiddenProperty(ModelClass, '_warned', {});
|
||||
|
||||
// inherit ModelBaseClass static methods
|
||||
for (var i in ModelBaseClass) {
|
||||
for (const i in ModelBaseClass) {
|
||||
// We need to skip properties that are already in the subclass, for example, the event emitter methods
|
||||
if (i !== '_mixins' && !(i in ModelClass)) {
|
||||
ModelClass[i] = ModelBaseClass[i];
|
||||
|
@ -239,7 +244,7 @@ ModelBuilder.prototype.define = function defineClass(className, properties, sett
|
|||
// Load and inject the model classes
|
||||
if (settings.models) {
|
||||
Object.keys(settings.models).forEach(function(m) {
|
||||
var model = settings.models[m];
|
||||
const model = settings.models[m];
|
||||
ModelClass[m] = typeof model === 'string' ? modelBuilder.getModel(model, true) : model;
|
||||
});
|
||||
}
|
||||
|
@ -247,7 +252,7 @@ ModelBuilder.prototype.define = function defineClass(className, properties, sett
|
|||
ModelClass.getter = {};
|
||||
ModelClass.setter = {};
|
||||
|
||||
for (var p in properties) {
|
||||
for (const p in properties) {
|
||||
// e.g excludePropertyList = ['id'] - base properties listed in excludePropertyList will be excluded from the model.
|
||||
// excludeBaseProperties is introduced in SOAP model generation only for now and below logic
|
||||
// handles excludeBaseProperties. Generated SOAP model has base as 'Model' which means 'id' property gets added
|
||||
|
@ -256,7 +261,7 @@ ModelBuilder.prototype.define = function defineClass(className, properties, sett
|
|||
// work either for SOAP generator case since generators use ModelDefinition.create to create property in the model
|
||||
// dynamically, that execution path has strict validation where doesn't accept 'id: false' in a property.
|
||||
// See https://github.com/strongloop/loopback-workspace/issues/486 for some more details.
|
||||
var excludePropertyList = settings['excludeBaseProperties'];
|
||||
const excludePropertyList = settings['excludeBaseProperties'];
|
||||
// Remove properties that reverted by the subclass of the property from excludePropertyList
|
||||
if (properties[p] === null || properties[p] === false ||
|
||||
(excludePropertyList != null && excludePropertyList.indexOf(p) != -1)) {
|
||||
|
@ -276,7 +281,7 @@ ModelBuilder.prototype.define = function defineClass(className, properties, sett
|
|||
}
|
||||
}
|
||||
|
||||
var modelDefinition = new ModelDefinition(this, className, properties, settings);
|
||||
const modelDefinition = new ModelDefinition(this, className, properties, settings);
|
||||
|
||||
this.definitions[className] = modelDefinition;
|
||||
|
||||
|
@ -285,13 +290,13 @@ ModelBuilder.prototype.define = function defineClass(className, properties, sett
|
|||
// keep a pointer to settings as models can use it for configuration
|
||||
ModelClass.settings = modelDefinition.settings;
|
||||
|
||||
var idInjection = settings.idInjection;
|
||||
let idInjection = settings.idInjection;
|
||||
if (idInjection !== false) {
|
||||
// Default to true if undefined
|
||||
idInjection = true;
|
||||
}
|
||||
|
||||
var idNames = modelDefinition.idNames();
|
||||
let idNames = modelDefinition.idNames();
|
||||
if (idNames.length > 0) {
|
||||
// id already exists
|
||||
idInjection = false;
|
||||
|
@ -306,11 +311,11 @@ ModelBuilder.prototype.define = function defineClass(className, properties, sett
|
|||
idNames = modelDefinition.idNames(); // Reload it after rebuild
|
||||
// Create a virtual property 'id'
|
||||
if (idNames.length === 1) {
|
||||
var idProp = idNames[0];
|
||||
const idProp = idNames[0];
|
||||
if (idProp !== 'id') {
|
||||
Object.defineProperty(ModelClass.prototype, 'id', {
|
||||
get: function() {
|
||||
var idProp = ModelClass.definition.idNames()[0];
|
||||
const idProp = ModelClass.definition.idNames()[0];
|
||||
return this.__data[idProp];
|
||||
},
|
||||
configurable: true,
|
||||
|
@ -321,9 +326,9 @@ ModelBuilder.prototype.define = function defineClass(className, properties, sett
|
|||
// Now the id property is an object that consists of multiple keys
|
||||
Object.defineProperty(ModelClass.prototype, 'id', {
|
||||
get: function() {
|
||||
var compositeId = {};
|
||||
var idNames = ModelClass.definition.idNames();
|
||||
for (var i = 0, p; i < idNames.length; i++) {
|
||||
const compositeId = {};
|
||||
const idNames = ModelClass.definition.idNames();
|
||||
for (let i = 0, p; i < idNames.length; i++) {
|
||||
p = idNames[i];
|
||||
compositeId[p] = this.__data[p];
|
||||
}
|
||||
|
@ -336,10 +341,10 @@ ModelBuilder.prototype.define = function defineClass(className, properties, sett
|
|||
|
||||
// updateOnly property is added to indicate that this property will appear in
|
||||
// the model for update/updateorcreate operations but and not for create operation.
|
||||
var forceId = ModelClass.settings.forceId;
|
||||
let forceId = ModelClass.settings.forceId;
|
||||
if (idNames.length > 0) {
|
||||
var idName = modelDefinition.idName();
|
||||
idProp = ModelClass.definition.rawProperties[idName];
|
||||
const idName = modelDefinition.idName();
|
||||
const idProp = ModelClass.definition.rawProperties[idName];
|
||||
if (idProp.generated && forceId !== false) {
|
||||
forceId = 'auto';
|
||||
} else if (!idProp.generated && forceId === 'auto') {
|
||||
|
@ -362,9 +367,9 @@ ModelBuilder.prototype.define = function defineClass(className, properties, sett
|
|||
|
||||
// A function to loop through the properties
|
||||
ModelClass.forEachProperty = function(cb) {
|
||||
var props = ModelClass.definition.properties;
|
||||
var keys = Object.keys(props);
|
||||
for (var i = 0, n = keys.length; i < n; i++) {
|
||||
const props = ModelClass.definition.properties;
|
||||
const keys = Object.keys(props);
|
||||
for (let i = 0, n = keys.length; i < n; i++) {
|
||||
cb(keys[i], props[keys[i]]);
|
||||
}
|
||||
};
|
||||
|
@ -396,15 +401,15 @@ ModelBuilder.prototype.define = function defineClass(className, properties, sett
|
|||
* merged with base model settings.
|
||||
*/
|
||||
ModelClass.extend = function(className, subClassProperties, subClassSettings) {
|
||||
var baseClassProperties = ModelClass.definition.properties;
|
||||
var baseClassSettings = ModelClass.definition.settings;
|
||||
const baseClassProperties = ModelClass.definition.properties;
|
||||
const baseClassSettings = ModelClass.definition.settings;
|
||||
|
||||
subClassProperties = subClassProperties || {};
|
||||
subClassSettings = subClassSettings || {};
|
||||
|
||||
// Check if subclass redefines the ids
|
||||
var idFound = false;
|
||||
for (var k in subClassProperties) {
|
||||
let idFound = false;
|
||||
for (const k in subClassProperties) {
|
||||
if (subClassProperties[k] && subClassProperties[k].id) {
|
||||
idFound = true;
|
||||
break;
|
||||
|
@ -412,17 +417,17 @@ ModelBuilder.prototype.define = function defineClass(className, properties, sett
|
|||
}
|
||||
|
||||
// Merging the properties
|
||||
var keys = Object.keys(baseClassProperties);
|
||||
for (var i = 0, n = keys.length; i < n; i++) {
|
||||
var key = keys[i];
|
||||
const keys = Object.keys(baseClassProperties);
|
||||
for (let i = 0, n = keys.length; i < n; i++) {
|
||||
const key = keys[i];
|
||||
|
||||
if (idFound && baseClassProperties[key].id) {
|
||||
// don't inherit id properties
|
||||
continue;
|
||||
}
|
||||
if (subClassProperties[key] === undefined) {
|
||||
var baseProp = baseClassProperties[key];
|
||||
var basePropCopy = baseProp;
|
||||
const baseProp = baseClassProperties[key];
|
||||
let basePropCopy = baseProp;
|
||||
if (baseProp && typeof baseProp === 'object') {
|
||||
// Deep clone the base properties
|
||||
basePropCopy = deepMerge(baseProp);
|
||||
|
@ -432,8 +437,8 @@ ModelBuilder.prototype.define = function defineClass(className, properties, sett
|
|||
}
|
||||
|
||||
// Merging the settings
|
||||
var originalSubclassSettings = subClassSettings;
|
||||
let mergePolicy = ModelClass.getMergePolicy(subClassSettings);
|
||||
const originalSubclassSettings = subClassSettings;
|
||||
const mergePolicy = ModelClass.getMergePolicy(subClassSettings);
|
||||
subClassSettings = mergeSettings(baseClassSettings, subClassSettings, mergePolicy);
|
||||
|
||||
// Ensure 'base' is not inherited. Note we don't have to delete 'super'
|
||||
|
@ -444,7 +449,7 @@ ModelBuilder.prototype.define = function defineClass(className, properties, sett
|
|||
}
|
||||
|
||||
// Define the subclass
|
||||
var subClass = modelBuilder.define(className, subClassProperties, subClassSettings, ModelClass);
|
||||
const subClass = modelBuilder.define(className, subClassProperties, subClassSettings, ModelClass);
|
||||
|
||||
// Calling the setup function
|
||||
if (typeof subClass.setup === 'function') {
|
||||
|
@ -492,7 +497,7 @@ ModelBuilder.prototype.define = function defineClass(className, properties, sett
|
|||
*/
|
||||
function mergeSettings(baseClassSettings, subClassSettings, mergePolicy) {
|
||||
// deep clone base class settings
|
||||
let mergedSettings = deepMerge(baseClassSettings);
|
||||
const mergedSettings = deepMerge(baseClassSettings);
|
||||
|
||||
Object.keys(baseClassSettings).forEach(function(key) {
|
||||
// rank base class settings arrays elements where required
|
||||
|
@ -545,15 +550,15 @@ ModelBuilder.prototype.define = function defineClass(className, properties, sett
|
|||
* @param {String} propertyName Name of the property.
|
||||
*/
|
||||
ModelClass.registerProperty = function(propertyName) {
|
||||
var properties = modelDefinition.build();
|
||||
var prop = properties[propertyName];
|
||||
var DataType = prop.type;
|
||||
const properties = modelDefinition.build();
|
||||
const prop = properties[propertyName];
|
||||
const DataType = prop.type;
|
||||
if (!DataType) {
|
||||
throw new Error(g.f('Invalid type for property %s', propertyName));
|
||||
}
|
||||
|
||||
if (prop.required) {
|
||||
var requiredOptions = typeof prop.required === 'object' ? prop.required : undefined;
|
||||
const requiredOptions = typeof prop.required === 'object' ? prop.required : undefined;
|
||||
ModelClass.validatesPresenceOf(propertyName, requiredOptions);
|
||||
}
|
||||
if (DataType === Date) ModelClass.validatesDateOf(propertyName);
|
||||
|
@ -567,7 +572,7 @@ ModelBuilder.prototype.define = function defineClass(className, properties, sett
|
|||
}
|
||||
},
|
||||
set: function(value) {
|
||||
var DataType = ModelClass.definition.properties[propertyName].type;
|
||||
let DataType = ModelClass.definition.properties[propertyName].type;
|
||||
if (Array.isArray(DataType) || DataType === Array) {
|
||||
DataType = List;
|
||||
} else if (DataType === Date) {
|
||||
|
@ -578,7 +583,7 @@ ModelBuilder.prototype.define = function defineClass(className, properties, sett
|
|||
DataType = modelBuilder.resolveType(DataType);
|
||||
}
|
||||
|
||||
var persistUndefinedAsNull = ModelClass.definition.settings.persistUndefinedAsNull;
|
||||
const persistUndefinedAsNull = ModelClass.definition.settings.persistUndefinedAsNull;
|
||||
if (value === undefined && persistUndefinedAsNull) {
|
||||
value = null;
|
||||
}
|
||||
|
@ -591,11 +596,19 @@ ModelBuilder.prototype.define = function defineClass(className, properties, sett
|
|||
this.__data[propertyName] = value;
|
||||
} else {
|
||||
if (DataType === List) {
|
||||
this.__data[propertyName] = DataType(value, properties[propertyName].type, this.__data);
|
||||
this.__data[propertyName] = isClass(DataType) ?
|
||||
new DataType(value, properties[propertyName].type, this) :
|
||||
DataType(value, properties[propertyName].type, this);
|
||||
} else {
|
||||
// Assume the type constructor handles Constructor() call
|
||||
// If not, we should call new DataType(value).valueOf();
|
||||
this.__data[propertyName] = (value instanceof DataType) ? value : DataType(value);
|
||||
this.__data[propertyName] = (value instanceof DataType) ?
|
||||
value :
|
||||
isClass(DataType) ? new DataType(value) : DataType(value);
|
||||
if (value && this.__data[propertyName] instanceof DefaultModelBaseClass) {
|
||||
// we are dealing with an embedded model, apply parent
|
||||
applyParentProperty(this.__data[propertyName], this);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -621,20 +634,20 @@ ModelBuilder.prototype.define = function defineClass(className, properties, sett
|
|||
});
|
||||
};
|
||||
|
||||
var props = ModelClass.definition.properties;
|
||||
var keys = Object.keys(props);
|
||||
var size = keys.length;
|
||||
for (i = 0; i < size; i++) {
|
||||
var propertyName = keys[i];
|
||||
const props = ModelClass.definition.properties;
|
||||
let keys = Object.keys(props);
|
||||
let size = keys.length;
|
||||
for (let i = 0; i < size; i++) {
|
||||
const propertyName = keys[i];
|
||||
ModelClass.registerProperty(propertyName);
|
||||
}
|
||||
|
||||
var mixinSettings = settings.mixins || {};
|
||||
const mixinSettings = settings.mixins || {};
|
||||
keys = Object.keys(mixinSettings);
|
||||
size = keys.length;
|
||||
for (i = 0; i < size; i++) {
|
||||
var name = keys[i];
|
||||
var mixin = mixinSettings[name];
|
||||
for (let i = 0; i < size; i++) {
|
||||
const name = keys[i];
|
||||
let mixin = mixinSettings[name];
|
||||
if (mixin === true) {
|
||||
mixin = {};
|
||||
}
|
||||
|
@ -696,7 +709,7 @@ function createModelClassCtor(name, ModelBaseClass) {
|
|||
// DataType for Date
|
||||
function DateType(arg) {
|
||||
if (arg === null) return null;
|
||||
var d = new Date(arg);
|
||||
const d = new Date(arg);
|
||||
return d;
|
||||
}
|
||||
|
||||
|
@ -770,19 +783,19 @@ ModelBuilder.prototype.defineValueType = function(type, aliases) {
|
|||
* @property {Boolean} index True if the property is an index; false otherwise.
|
||||
*/
|
||||
ModelBuilder.prototype.extendModel = function(model, props) {
|
||||
var t = this;
|
||||
var keys = Object.keys(props);
|
||||
for (var i = 0; i < keys.length; i++) {
|
||||
var definition = props[keys[i]];
|
||||
const t = this;
|
||||
const keys = Object.keys(props);
|
||||
for (let i = 0; i < keys.length; i++) {
|
||||
const definition = props[keys[i]];
|
||||
t.defineProperty(model, keys[i], definition);
|
||||
}
|
||||
};
|
||||
|
||||
ModelBuilder.prototype.copyModel = function copyModel(Master) {
|
||||
var modelBuilder = this;
|
||||
var className = Master.modelName;
|
||||
var md = Master.modelBuilder.definitions[className];
|
||||
var Slave = function SlaveModel() {
|
||||
const modelBuilder = this;
|
||||
const className = Master.modelName;
|
||||
const md = Master.modelBuilder.definitions[className];
|
||||
const Slave = function SlaveModel() {
|
||||
Master.apply(this, [].slice.call(arguments));
|
||||
};
|
||||
|
||||
|
@ -849,47 +862,46 @@ ModelBuilder.prototype.getSchemaName = function(name) {
|
|||
/**
|
||||
* Resolve the type string to be a function, for example, 'String' to String.
|
||||
* Returns {Function} if the type is resolved
|
||||
* @param {String} type The type string, such as 'number', 'Number', 'boolean',
|
||||
* or 'String'. This parameter is case insensitive.
|
||||
* @param {String | Object | Array} prop The object whose type is to be resolved
|
||||
*/
|
||||
ModelBuilder.prototype.resolveType = function(type) {
|
||||
if (!type) {
|
||||
return type;
|
||||
ModelBuilder.prototype.resolveType = function(prop, isSubProperty) {
|
||||
if (!prop) {
|
||||
return prop;
|
||||
}
|
||||
if (Array.isArray(type) && type.length > 0) {
|
||||
if (Array.isArray(prop) && prop.length > 0) {
|
||||
// For array types, the first item should be the type string
|
||||
var itemType = this.resolveType(type[0]);
|
||||
const itemType = this.resolveType(prop[0]);
|
||||
if (typeof itemType === 'function') {
|
||||
return [itemType];
|
||||
} else {
|
||||
return itemType; // Not resolved, return the type string
|
||||
}
|
||||
}
|
||||
if (typeof type === 'string') {
|
||||
var schemaType = ModelBuilder.schemaTypes[type.toLowerCase()] || this.models[type];
|
||||
if (typeof prop === 'string') {
|
||||
const schemaType = ModelBuilder.schemaTypes[prop.toLowerCase()] || this.models[prop];
|
||||
if (schemaType) {
|
||||
return schemaType;
|
||||
} else {
|
||||
// The type cannot be resolved, let's create a place holder
|
||||
type = this.define(type, {}, {unresolved: true});
|
||||
return type;
|
||||
prop = this.define(prop, {}, {unresolved: true});
|
||||
return prop;
|
||||
}
|
||||
} else if (type.constructor.name === 'Object') {
|
||||
} else if (prop.constructor.name === 'Object') {
|
||||
// We also support the syntax {type: 'string', ...}
|
||||
if (type.type) {
|
||||
return this.resolveType(type.type);
|
||||
if (!isSubProperty && prop.type) {
|
||||
return this.resolveType(prop.type, true);
|
||||
} else {
|
||||
return this.define(this.getSchemaName(null),
|
||||
type, {
|
||||
prop, {
|
||||
anonymous: true,
|
||||
idInjection: false,
|
||||
strict: this.settings.strictEmbeddedModels || false,
|
||||
});
|
||||
}
|
||||
} else if ('function' === typeof type) {
|
||||
return type;
|
||||
} else if ('function' === typeof prop) {
|
||||
return prop;
|
||||
}
|
||||
return type;
|
||||
return prop;
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -906,7 +918,7 @@ ModelBuilder.prototype.resolveType = function(type) {
|
|||
* model name.
|
||||
*/
|
||||
ModelBuilder.prototype.buildModels = function(schemas, createModel) {
|
||||
var models = {};
|
||||
const models = {};
|
||||
|
||||
// Normalize the schemas to be an array of the schema objects {name: <name>, properties: {}, options: {}}
|
||||
if (!Array.isArray(schemas)) {
|
||||
|
@ -925,25 +937,22 @@ ModelBuilder.prototype.buildModels = function(schemas, createModel) {
|
|||
}
|
||||
}
|
||||
|
||||
var relations = [];
|
||||
for (var s = 0, n = schemas.length; s < n; s++) {
|
||||
var name = this.getSchemaName(schemas[s].name);
|
||||
let relations = [];
|
||||
for (let s = 0, n = schemas.length; s < n; s++) {
|
||||
const name = this.getSchemaName(schemas[s].name);
|
||||
schemas[s].name = name;
|
||||
var model;
|
||||
if (typeof createModel === 'function') {
|
||||
model = createModel(schemas[s].name, schemas[s].properties, schemas[s].options);
|
||||
} else {
|
||||
model = this.define(schemas[s].name, schemas[s].properties, schemas[s].options);
|
||||
}
|
||||
const model = typeof createModel === 'function' ?
|
||||
createModel(schemas[s].name, schemas[s].properties, schemas[s].options) :
|
||||
this.define(schemas[s].name, schemas[s].properties, schemas[s].options);
|
||||
models[name] = model;
|
||||
relations = relations.concat(model.definition.relations);
|
||||
}
|
||||
|
||||
// Connect the models based on the relations
|
||||
for (var i = 0; i < relations.length; i++) {
|
||||
var relation = relations[i];
|
||||
var sourceModel = models[relation.source];
|
||||
var targetModel = models[relation.target];
|
||||
for (let i = 0; i < relations.length; i++) {
|
||||
const relation = relations[i];
|
||||
const sourceModel = models[relation.source];
|
||||
const targetModel = models[relation.target];
|
||||
if (sourceModel && targetModel) {
|
||||
if (typeof sourceModel[relation.type] === 'function') {
|
||||
sourceModel[relation.type](targetModel, {as: relation.as});
|
||||
|
@ -962,7 +971,7 @@ ModelBuilder.prototype.buildModels = function(schemas, createModel) {
|
|||
*/
|
||||
ModelBuilder.prototype.buildModelFromInstance = function(name, json, options) {
|
||||
// Introspect the JSON document to generate a schema
|
||||
var schema = introspect(json);
|
||||
const schema = introspect(json);
|
||||
|
||||
// Create a model for the generated schema
|
||||
return this.define(name, schema, options);
|
||||
|
|
|
@ -1,15 +1,16 @@
|
|||
// Copyright IBM Corp. 2013,2016. All Rights Reserved.
|
||||
// Copyright IBM Corp. 2013,2019. All Rights Reserved.
|
||||
// Node module: loopback-datasource-juggler
|
||||
// This file is licensed under the MIT License.
|
||||
// License text available at https://opensource.org/licenses/MIT
|
||||
|
||||
'use strict';
|
||||
|
||||
var assert = require('assert');
|
||||
var util = require('util');
|
||||
var EventEmitter = require('events').EventEmitter;
|
||||
var traverse = require('traverse');
|
||||
var ModelBaseClass = require('./model');
|
||||
var ModelBuilder = require('./model-builder');
|
||||
const assert = require('assert');
|
||||
const util = require('util');
|
||||
const EventEmitter = require('events').EventEmitter;
|
||||
const traverse = require('traverse');
|
||||
const ModelBaseClass = require('./model');
|
||||
const ModelBuilder = require('./model-builder');
|
||||
|
||||
/**
|
||||
* Model definition
|
||||
|
@ -35,7 +36,7 @@ function ModelDefinition(modelBuilder, name, properties, settings) {
|
|||
assert(name, 'name is missing');
|
||||
|
||||
if (arguments.length === 2 && typeof name === 'object') {
|
||||
var schema = name;
|
||||
const schema = name;
|
||||
this.name = schema.name;
|
||||
this.rawProperties = schema.properties || {}; // Keep the raw property definitions
|
||||
this.settings = schema.settings || {};
|
||||
|
@ -60,7 +61,7 @@ require('./types')(ModelDefinition);
|
|||
* @param {String} connectorType The connector type, such as 'oracle' or 'mongodb'
|
||||
*/
|
||||
ModelDefinition.prototype.tableName = function(connectorType) {
|
||||
var settings = this.settings;
|
||||
const settings = this.settings;
|
||||
if (settings[connectorType]) {
|
||||
return settings[connectorType].table || settings[connectorType].tableName || this.name;
|
||||
} else {
|
||||
|
@ -79,7 +80,7 @@ ModelDefinition.prototype.columnName = function(connectorType, propertyName) {
|
|||
return propertyName;
|
||||
}
|
||||
this.build();
|
||||
var property = this.properties[propertyName];
|
||||
const property = this.properties[propertyName];
|
||||
if (property && property[connectorType]) {
|
||||
return property[connectorType].column || property[connectorType].columnName || propertyName;
|
||||
} else {
|
||||
|
@ -98,7 +99,7 @@ ModelDefinition.prototype.columnMetadata = function(connectorType, propertyName)
|
|||
return propertyName;
|
||||
}
|
||||
this.build();
|
||||
var property = this.properties[propertyName];
|
||||
const property = this.properties[propertyName];
|
||||
if (property && property[connectorType]) {
|
||||
return property[connectorType];
|
||||
} else {
|
||||
|
@ -113,9 +114,9 @@ ModelDefinition.prototype.columnMetadata = function(connectorType, propertyName)
|
|||
*/
|
||||
ModelDefinition.prototype.columnNames = function(connectorType) {
|
||||
this.build();
|
||||
var props = this.properties;
|
||||
var cols = [];
|
||||
for (var p in props) {
|
||||
const props = this.properties;
|
||||
const cols = [];
|
||||
for (const p in props) {
|
||||
if (props[p][connectorType]) {
|
||||
cols.push(props[p][connectorType].column || props[p][connectorType].columnName || p);
|
||||
} else {
|
||||
|
@ -133,11 +134,11 @@ ModelDefinition.prototype.ids = function() {
|
|||
if (this._ids) {
|
||||
return this._ids;
|
||||
}
|
||||
var ids = [];
|
||||
const ids = [];
|
||||
this.build();
|
||||
var props = this.properties;
|
||||
for (var key in props) {
|
||||
var id = props[key].id;
|
||||
const props = this.properties;
|
||||
for (const key in props) {
|
||||
let id = props[key].id;
|
||||
if (!id) {
|
||||
continue;
|
||||
}
|
||||
|
@ -167,7 +168,7 @@ ModelDefinition.prototype.idColumnName = function(connectorType) {
|
|||
* @returns {String} property name for ID
|
||||
*/
|
||||
ModelDefinition.prototype.idName = function() {
|
||||
var id = this.ids()[0];
|
||||
const id = this.ids()[0];
|
||||
if (this.properties.id && this.properties.id.id) {
|
||||
return 'id';
|
||||
} else {
|
||||
|
@ -180,8 +181,8 @@ ModelDefinition.prototype.idName = function() {
|
|||
* @returns {String[]} property names for IDs
|
||||
*/
|
||||
ModelDefinition.prototype.idNames = function() {
|
||||
var ids = this.ids();
|
||||
var names = ids.map(function(id) {
|
||||
const ids = this.ids();
|
||||
const names = ids.map(function(id) {
|
||||
return id.name;
|
||||
});
|
||||
return names;
|
||||
|
@ -193,13 +194,13 @@ ModelDefinition.prototype.idNames = function() {
|
|||
*/
|
||||
ModelDefinition.prototype.indexes = function() {
|
||||
this.build();
|
||||
var indexes = {};
|
||||
const indexes = {};
|
||||
if (this.settings.indexes) {
|
||||
for (var i in this.settings.indexes) {
|
||||
for (const i in this.settings.indexes) {
|
||||
indexes[i] = this.settings.indexes[i];
|
||||
}
|
||||
}
|
||||
for (var p in this.properties) {
|
||||
for (const p in this.properties) {
|
||||
if (this.properties[p].index) {
|
||||
indexes[p + '_index'] = this.properties[p].index;
|
||||
}
|
||||
|
@ -222,9 +223,9 @@ ModelDefinition.prototype.build = function(forceRebuild) {
|
|||
return this.properties;
|
||||
}
|
||||
this.properties = {};
|
||||
for (var p in this.rawProperties) {
|
||||
var prop = this.rawProperties[p];
|
||||
var type = this.modelBuilder.resolveType(prop);
|
||||
for (const p in this.rawProperties) {
|
||||
const prop = this.rawProperties[p];
|
||||
const type = this.modelBuilder.resolveType(prop);
|
||||
if (typeof type === 'string') {
|
||||
this.relations.push({
|
||||
source: this.name,
|
||||
|
@ -233,11 +234,11 @@ ModelDefinition.prototype.build = function(forceRebuild) {
|
|||
as: p,
|
||||
});
|
||||
} else {
|
||||
var typeDef = {
|
||||
const typeDef = {
|
||||
type: type,
|
||||
};
|
||||
if (typeof prop === 'object' && prop !== null) {
|
||||
for (var a in prop) {
|
||||
for (const a in prop) {
|
||||
// Skip the type property but don't delete it Model.extend() shares same instances of the properties from the base class
|
||||
if (a !== 'type') {
|
||||
typeDef[a] = prop[a];
|
||||
|
@ -274,14 +275,14 @@ ModelDefinition.prototype.toJSON = function(forceRebuild) {
|
|||
if (this.json) {
|
||||
return this.json;
|
||||
}
|
||||
var json = {
|
||||
const json = {
|
||||
name: this.name,
|
||||
properties: {},
|
||||
settings: this.settings,
|
||||
};
|
||||
this.build(forceRebuild);
|
||||
|
||||
var mapper = function(val) {
|
||||
const mapper = function(val) {
|
||||
if (val === undefined || val === null) {
|
||||
return val;
|
||||
}
|
||||
|
@ -302,7 +303,7 @@ ModelDefinition.prototype.toJSON = function(forceRebuild) {
|
|||
return val;
|
||||
}
|
||||
};
|
||||
for (var p in this.properties) {
|
||||
for (const p in this.properties) {
|
||||
json.properties[p] = traverse(this.properties[p]).map(mapper);
|
||||
}
|
||||
this.json = json;
|
||||
|
|
|
@ -0,0 +1,586 @@
|
|||
// Copyright IBM Corp. 2018,2019. All Rights Reserved.
|
||||
// Node module: loopback-datasource-juggler
|
||||
// This file is licensed under the MIT License.
|
||||
// License text available at https://opensource.org/licenses/MIT
|
||||
|
||||
// Turning on strict for this file breaks lots of test cases;
|
||||
// disabling strict for this file
|
||||
/* eslint-disable strict */
|
||||
|
||||
module.exports = ModelUtils;
|
||||
|
||||
/*!
|
||||
* Module dependencies
|
||||
*/
|
||||
const g = require('strong-globalize')();
|
||||
const geo = require('./geo');
|
||||
const {
|
||||
fieldsToArray,
|
||||
sanitizeQuery: sanitizeQueryOrData,
|
||||
isPlainObject,
|
||||
isClass,
|
||||
toRegExp,
|
||||
} = require('./utils');
|
||||
const BaseModel = require('./model');
|
||||
|
||||
/**
|
||||
* A mixin to contain utility methods for DataAccessObject
|
||||
*/
|
||||
function ModelUtils() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify if allowExtendedOperators is enabled
|
||||
* @options {Object} [options] Optional options to use.
|
||||
* @property {Boolean} allowExtendedOperators.
|
||||
* @returns {Boolean} Returns `true` if allowExtendedOperators is enabled, else `false`.
|
||||
*/
|
||||
ModelUtils._allowExtendedOperators = function(options) {
|
||||
const flag = this._getSetting('allowExtendedOperators', options);
|
||||
if (flag != null) return !!flag;
|
||||
// Default to `false`
|
||||
return false;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get settings via hierarchical determination
|
||||
* - method level options
|
||||
* - model level settings
|
||||
* - data source level settings
|
||||
*
|
||||
* @param {String} key The setting key
|
||||
*/
|
||||
ModelUtils._getSetting = function(key, options) {
|
||||
// Check method level options
|
||||
let val = options && options[key];
|
||||
if (val !== undefined) return val;
|
||||
// Check for settings in model
|
||||
const m = this.definition;
|
||||
if (m && m.settings) {
|
||||
val = m.settings[key];
|
||||
if (val !== undefined) {
|
||||
return m.settings[key];
|
||||
}
|
||||
// Fall back to data source level
|
||||
}
|
||||
|
||||
// Check for settings in connector
|
||||
const ds = this.getDataSource();
|
||||
if (ds && ds.settings) {
|
||||
return ds.settings[key];
|
||||
}
|
||||
|
||||
return undefined;
|
||||
};
|
||||
|
||||
const operators = {
|
||||
eq: '=',
|
||||
gt: '>',
|
||||
gte: '>=',
|
||||
lt: '<',
|
||||
lte: '<=',
|
||||
between: 'BETWEEN',
|
||||
inq: 'IN',
|
||||
nin: 'NOT IN',
|
||||
neq: '!=',
|
||||
like: 'LIKE',
|
||||
nlike: 'NOT LIKE',
|
||||
ilike: 'ILIKE',
|
||||
nilike: 'NOT ILIKE',
|
||||
regexp: 'REGEXP',
|
||||
};
|
||||
|
||||
/*
|
||||
* Normalize the filter object and throw errors if invalid values are detected
|
||||
* @param {Object} filter The query filter object
|
||||
* @options {Object} [options] Optional options to use.
|
||||
* @property {Boolean} allowExtendedOperators.
|
||||
* @returns {Object} The normalized filter object
|
||||
* @private
|
||||
*/
|
||||
ModelUtils._normalize = function(filter, options) {
|
||||
if (!filter) {
|
||||
return undefined;
|
||||
}
|
||||
let err = null;
|
||||
if ((typeof filter !== 'object') || Array.isArray(filter)) {
|
||||
err = new Error(g.f('The query filter %j is not an {{object}}', filter));
|
||||
err.statusCode = 400;
|
||||
throw err;
|
||||
}
|
||||
if (filter.limit || filter.skip || filter.offset) {
|
||||
const limit = Number(filter.limit || 100);
|
||||
const offset = Number(filter.skip || filter.offset || 0);
|
||||
if (isNaN(limit) || limit <= 0 || Math.ceil(limit) !== limit) {
|
||||
err = new Error(g.f('The {{limit}} parameter %j is not valid',
|
||||
filter.limit));
|
||||
err.statusCode = 400;
|
||||
throw err;
|
||||
}
|
||||
if (isNaN(offset) || offset < 0 || Math.ceil(offset) !== offset) {
|
||||
err = new Error(g.f('The {{offset/skip}} parameter %j is not valid',
|
||||
filter.skip || filter.offset));
|
||||
err.statusCode = 400;
|
||||
throw err;
|
||||
}
|
||||
filter.limit = limit;
|
||||
filter.offset = offset;
|
||||
filter.skip = offset;
|
||||
}
|
||||
|
||||
if (filter.order) {
|
||||
let order = filter.order;
|
||||
if (!Array.isArray(order)) {
|
||||
order = [order];
|
||||
}
|
||||
const fields = [];
|
||||
for (let i = 0, m = order.length; i < m; i++) {
|
||||
if (typeof order[i] === 'string') {
|
||||
// Normalize 'f1 ASC, f2 DESC, f3' to ['f1 ASC', 'f2 DESC', 'f3']
|
||||
const tokens = order[i].split(/(?:\s*,\s*)+/);
|
||||
for (let t = 0, n = tokens.length; t < n; t++) {
|
||||
let token = tokens[t];
|
||||
if (token.length === 0) {
|
||||
// Skip empty token
|
||||
continue;
|
||||
}
|
||||
const parts = token.split(/\s+/);
|
||||
if (parts.length >= 2) {
|
||||
const dir = parts[1].toUpperCase();
|
||||
if (dir === 'ASC' || dir === 'DESC') {
|
||||
token = parts[0] + ' ' + dir;
|
||||
} else {
|
||||
err = new Error(g.f('The {{order}} %j has invalid direction', token));
|
||||
err.statusCode = 400;
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
fields.push(token);
|
||||
}
|
||||
} else {
|
||||
err = new Error(g.f('The order %j is not valid', order[i]));
|
||||
err.statusCode = 400;
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
if (fields.length === 1 && typeof filter.order === 'string') {
|
||||
filter.order = fields[0];
|
||||
} else {
|
||||
filter.order = fields;
|
||||
}
|
||||
}
|
||||
|
||||
// normalize fields as array of included property names
|
||||
if (filter.fields) {
|
||||
filter.fields = fieldsToArray(filter.fields,
|
||||
Object.keys(this.definition.properties), this.settings.strict);
|
||||
}
|
||||
|
||||
filter = this._sanitizeQuery(filter, options);
|
||||
this._coerce(filter.where, options);
|
||||
return filter;
|
||||
};
|
||||
|
||||
function DateType(arg) {
|
||||
const d = new Date(arg);
|
||||
if (isNaN(d.getTime())) {
|
||||
throw new Error(g.f('Invalid date: %s', arg));
|
||||
}
|
||||
return d;
|
||||
}
|
||||
|
||||
function BooleanType(arg) {
|
||||
if (typeof arg === 'string') {
|
||||
switch (arg) {
|
||||
case 'true':
|
||||
case '1':
|
||||
return true;
|
||||
case 'false':
|
||||
case '0':
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (arg == null) {
|
||||
return null;
|
||||
}
|
||||
return Boolean(arg);
|
||||
}
|
||||
|
||||
function NumberType(val) {
|
||||
const num = Number(val);
|
||||
return !isNaN(num) ? num : val;
|
||||
}
|
||||
|
||||
function coerceArray(val) {
|
||||
if (Array.isArray(val)) {
|
||||
return val;
|
||||
}
|
||||
|
||||
if (!isPlainObject(val)) {
|
||||
throw new Error(g.f('Value is not an {{array}} or {{object}} with sequential numeric indices'));
|
||||
}
|
||||
|
||||
// It is an object, check if empty
|
||||
const props = Object.keys(val);
|
||||
|
||||
if (props.length === 0) {
|
||||
throw new Error(g.f('Value is an empty {{object}}'));
|
||||
}
|
||||
|
||||
const arrayVal = new Array(props.length);
|
||||
for (let i = 0; i < arrayVal.length; ++i) {
|
||||
if (!val.hasOwnProperty(i)) {
|
||||
throw new Error(g.f('Value is not an {{array}} or {{object}} with sequential numeric indices'));
|
||||
}
|
||||
|
||||
arrayVal[i] = val[i];
|
||||
}
|
||||
|
||||
return arrayVal;
|
||||
}
|
||||
|
||||
function _normalizeAsArray(result) {
|
||||
if (typeof result === 'string') {
|
||||
result = [result];
|
||||
}
|
||||
if (Array.isArray(result)) {
|
||||
return result;
|
||||
} else {
|
||||
// See https://github.com/strongloop/loopback-datasource-juggler/issues/1646
|
||||
// `ModelBaseClass` normalize the properties to an object such as `{secret: true}`
|
||||
const keys = [];
|
||||
for (const k in result) {
|
||||
if (result[k]) keys.push(k);
|
||||
}
|
||||
return keys;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an array of hidden property names
|
||||
*/
|
||||
ModelUtils._getHiddenProperties = function() {
|
||||
const settings = this.definition.settings || {};
|
||||
const result = settings.hiddenProperties || settings.hidden || [];
|
||||
return _normalizeAsArray(result);
|
||||
};
|
||||
|
||||
/**
|
||||
* Get an array of protected property names
|
||||
*/
|
||||
ModelUtils._getProtectedProperties = function() {
|
||||
const settings = this.definition.settings || {};
|
||||
const result = settings.protectedProperties || settings.protected || [];
|
||||
return _normalizeAsArray(result);
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the maximum depth of a query object
|
||||
*/
|
||||
ModelUtils._getMaxDepthOfQuery = function(options, defaultValue) {
|
||||
options = options || {};
|
||||
// See https://github.com/strongloop/loopback-datasource-juggler/issues/1651
|
||||
let maxDepth = this._getSetting('maxDepthOfQuery', options);
|
||||
if (maxDepth == null) {
|
||||
maxDepth = defaultValue || 32;
|
||||
}
|
||||
return +maxDepth;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the maximum depth of a data object
|
||||
*/
|
||||
ModelUtils._getMaxDepthOfData = function(options, defaultValue) {
|
||||
options = options || {};
|
||||
// See https://github.com/strongloop/loopback-datasource-juggler/issues/1651
|
||||
let maxDepth = this._getSetting('maxDepthOfData', options);
|
||||
if (maxDepth == null) {
|
||||
maxDepth = defaultValue || 64;
|
||||
}
|
||||
return +maxDepth;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the prohibitHiddenPropertiesInQuery flag
|
||||
*/
|
||||
ModelUtils._getProhibitHiddenPropertiesInQuery = function(options, defaultValue) {
|
||||
const flag = this._getSetting('prohibitHiddenPropertiesInQuery', options);
|
||||
if (flag == null) return !!defaultValue;
|
||||
return !!flag;
|
||||
};
|
||||
|
||||
/**
|
||||
* Sanitize the query object
|
||||
*/
|
||||
ModelUtils._sanitizeQuery = function(query, options) {
|
||||
options = options || {};
|
||||
|
||||
// Get settings to normalize `undefined` values
|
||||
const normalizeUndefinedInQuery = this._getSetting('normalizeUndefinedInQuery', options);
|
||||
// Get setting to prohibit hidden/protected properties in query
|
||||
const prohibitHiddenPropertiesInQuery = this._getProhibitHiddenPropertiesInQuery(options);
|
||||
|
||||
// See https://github.com/strongloop/loopback-datasource-juggler/issues/1651
|
||||
const maxDepthOfQuery = this._getMaxDepthOfQuery(options);
|
||||
|
||||
let prohibitedKeys = [];
|
||||
// Check violation of keys
|
||||
if (prohibitHiddenPropertiesInQuery) {
|
||||
prohibitedKeys = this._getHiddenProperties();
|
||||
if (options.prohibitProtectedPropertiesInQuery) {
|
||||
prohibitedKeys = prohibitedKeys.concat(this._getProtectedProperties());
|
||||
}
|
||||
}
|
||||
return sanitizeQueryOrData(query,
|
||||
Object.assign({
|
||||
maxDepth: maxDepthOfQuery,
|
||||
prohibitedKeys: prohibitedKeys,
|
||||
normalizeUndefinedInQuery: normalizeUndefinedInQuery,
|
||||
}, options));
|
||||
};
|
||||
|
||||
/**
|
||||
* Sanitize the data object
|
||||
*/
|
||||
ModelUtils._sanitizeData = function(data, options) {
|
||||
options = options || {};
|
||||
return sanitizeQueryOrData(data,
|
||||
Object.assign({
|
||||
maxDepth: this._getMaxDepthOfData(options),
|
||||
}, options));
|
||||
};
|
||||
|
||||
/*
|
||||
* Coerce values based the property types
|
||||
* @param {Object} where The where clause
|
||||
* @options {Object} [options] Optional options to use.
|
||||
* @param {Object} Optional model definition to use.
|
||||
* @property {Boolean} allowExtendedOperators.
|
||||
* @returns {Object} The coerced where clause
|
||||
* @private
|
||||
*/
|
||||
ModelUtils._coerce = function(where, options, modelDef) {
|
||||
const self = this;
|
||||
if (where == null) {
|
||||
return where;
|
||||
}
|
||||
options = options || {};
|
||||
|
||||
let err;
|
||||
if (typeof where !== 'object' || Array.isArray(where)) {
|
||||
err = new Error(g.f('The where clause %j is not an {{object}}', where));
|
||||
err.statusCode = 400;
|
||||
throw err;
|
||||
}
|
||||
let props;
|
||||
if (modelDef && modelDef.properties) {
|
||||
props = modelDef.properties;
|
||||
} else {
|
||||
props = self.definition.properties;
|
||||
}
|
||||
|
||||
for (const p in where) {
|
||||
// Handle logical operators
|
||||
if (p === 'and' || p === 'or' || p === 'nor') {
|
||||
let clauses = where[p];
|
||||
try {
|
||||
clauses = coerceArray(clauses);
|
||||
} catch (e) {
|
||||
err = new Error(g.f('The %s operator has invalid clauses %j: %s', p, clauses, e.message));
|
||||
err.statusCode = 400;
|
||||
throw err;
|
||||
}
|
||||
|
||||
for (let k = 0; k < clauses.length; k++) {
|
||||
self._coerce(clauses[k], options);
|
||||
}
|
||||
|
||||
where[p] = clauses;
|
||||
|
||||
continue;
|
||||
}
|
||||
let DataType = props[p] && props[p].type;
|
||||
if (!DataType) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ((Array.isArray(DataType) || DataType === Array) && !isNestedModel(DataType)) {
|
||||
DataType = DataType[0];
|
||||
}
|
||||
if (DataType === Date) {
|
||||
DataType = DateType;
|
||||
} else if (DataType === Boolean) {
|
||||
DataType = BooleanType;
|
||||
} else if (DataType === Number) {
|
||||
// This fixes a regression in mongodb connector
|
||||
// For numbers, only convert it produces a valid number
|
||||
// LoopBack by default injects a number id. We should fix it based
|
||||
// on the connector's input, for example, MongoDB should use string
|
||||
// while RDBs typically use number
|
||||
DataType = NumberType;
|
||||
}
|
||||
|
||||
if (!DataType) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (DataType === geo.GeoPoint) {
|
||||
// Skip the GeoPoint as the near operator breaks the assumption that
|
||||
// an operation has only one property
|
||||
// We should probably fix it based on
|
||||
// http://docs.mongodb.org/manual/reference/operator/query/near/
|
||||
// The other option is to make operators start with $
|
||||
continue;
|
||||
}
|
||||
|
||||
let val = where[p];
|
||||
if (val === null || val === undefined) {
|
||||
continue;
|
||||
}
|
||||
// Check there is an operator
|
||||
let operator = null;
|
||||
const exp = val;
|
||||
if (val.constructor === Object) {
|
||||
for (const op in operators) {
|
||||
if (op in val) {
|
||||
val = val[op];
|
||||
operator = op;
|
||||
switch (operator) {
|
||||
case 'inq':
|
||||
case 'nin':
|
||||
case 'between':
|
||||
try {
|
||||
val = coerceArray(val);
|
||||
} catch (e) {
|
||||
err = new Error(g.f('The %s property has invalid clause %j: %s', p, where[p], e));
|
||||
err.statusCode = 400;
|
||||
throw err;
|
||||
}
|
||||
|
||||
if (operator === 'between' && val.length !== 2) {
|
||||
err = new Error(g.f(
|
||||
'The %s property has invalid clause %j: Expected precisely 2 values, received %d',
|
||||
p,
|
||||
where[p],
|
||||
val.length,
|
||||
));
|
||||
err.statusCode = 400;
|
||||
throw err;
|
||||
}
|
||||
break;
|
||||
case 'like':
|
||||
case 'nlike':
|
||||
case 'ilike':
|
||||
case 'nilike':
|
||||
if (!(typeof val === 'string' || val instanceof RegExp)) {
|
||||
err = new Error(g.f(
|
||||
'The %s property has invalid clause %j: Expected a string or RegExp',
|
||||
p,
|
||||
where[p],
|
||||
));
|
||||
err.statusCode = 400;
|
||||
throw err;
|
||||
}
|
||||
break;
|
||||
case 'regexp':
|
||||
val = toRegExp(val);
|
||||
if (val instanceof Error) {
|
||||
val.statusCode = 400;
|
||||
throw val;
|
||||
}
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
// Coerce val into an array if it resembles an array-like object
|
||||
val = coerceArray(val);
|
||||
} catch (e) {
|
||||
// NOOP when not coercable into an array.
|
||||
}
|
||||
|
||||
const allowExtendedOperators = self._allowExtendedOperators(options);
|
||||
// Coerce the array items
|
||||
if (Array.isArray(val) && !isNestedModel(DataType)) {
|
||||
for (let i = 0; i < val.length; i++) {
|
||||
if (val[i] !== null && val[i] !== undefined) {
|
||||
if (!(val[i] instanceof RegExp)) {
|
||||
val[i] = isClass(DataType) ? new DataType(val[i]) : DataType(val[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (val != null) {
|
||||
if (operator === null && val instanceof RegExp) {
|
||||
// Normalize {name: /A/} to {name: {regexp: /A/}}
|
||||
operator = 'regexp';
|
||||
} else if (operator === 'regexp' && val instanceof RegExp) {
|
||||
// Do not coerce regex literals/objects
|
||||
} else if ((operator === 'like' || operator === 'nlike' ||
|
||||
operator === 'ilike' || operator === 'nilike') && val instanceof RegExp) {
|
||||
// Do not coerce RegExp operator value
|
||||
} else if (allowExtendedOperators && typeof val === 'object') {
|
||||
// Do not coerce object values when extended operators are allowed
|
||||
} else {
|
||||
if (!allowExtendedOperators) {
|
||||
const extendedOperators = Object.keys(val).filter(function(k) {
|
||||
return k[0] === '$';
|
||||
});
|
||||
if (extendedOperators.length) {
|
||||
const msg = g.f('Operators "' + extendedOperators.join(', ') + '" are not allowed in query');
|
||||
const err = new Error(msg);
|
||||
err.code = 'OPERATOR_NOT_ALLOWED_IN_QUERY';
|
||||
err.statusCode = 400;
|
||||
err.details = {
|
||||
operators: extendedOperators,
|
||||
where: where,
|
||||
};
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
if (isNestedModel(DataType)) {
|
||||
if (Array.isArray(DataType) && Array.isArray(val)) {
|
||||
if (val === null || val === undefined) continue;
|
||||
for (const it of val) {
|
||||
self._coerce(it, options, DataType[0].definition);
|
||||
}
|
||||
} else {
|
||||
self._coerce(val, options, DataType.definition);
|
||||
}
|
||||
continue;
|
||||
} else {
|
||||
val = isClass(DataType) ? new DataType(val) : DataType(val);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// Rebuild {property: {operator: value}}
|
||||
if (operator && operator !== 'eq') {
|
||||
const value = {};
|
||||
value[operator] = val;
|
||||
if (exp.options) {
|
||||
// Keep options for operators
|
||||
value.options = exp.options;
|
||||
}
|
||||
val = value;
|
||||
}
|
||||
where[p] = val;
|
||||
}
|
||||
return where;
|
||||
};
|
||||
|
||||
/**
|
||||
* A utility function which checks for nested property definitions
|
||||
*
|
||||
* @param {*} propType Property type metadata
|
||||
*
|
||||
*/
|
||||
function isNestedModel(propType) {
|
||||
if (!propType) return false;
|
||||
if (Array.isArray(propType)) return isNestedModel(propType[0]);
|
||||
return propType.hasOwnProperty('definition') && propType.definition.hasOwnProperty('properties');
|
||||
}
|
||||
|
220
lib/model.js
220
lib/model.js
|
@ -1,4 +1,4 @@
|
|||
// Copyright IBM Corp. 2013,2016. All Rights Reserved.
|
||||
// Copyright IBM Corp. 2013,2019. All Rights Reserved.
|
||||
// Node module: loopback-datasource-juggler
|
||||
// This file is licensed under the MIT License.
|
||||
// License text available at https://opensource.org/licenses/MIT
|
||||
|
@ -16,20 +16,22 @@ module.exports = ModelBaseClass;
|
|||
* Module dependencies
|
||||
*/
|
||||
|
||||
var g = require('strong-globalize')();
|
||||
var util = require('util');
|
||||
var jutil = require('./jutil');
|
||||
var List = require('./list');
|
||||
var Hookable = require('./hooks');
|
||||
var validations = require('./validations');
|
||||
var _extend = util._extend;
|
||||
var utils = require('./utils');
|
||||
var fieldsToArray = utils.fieldsToArray;
|
||||
var uuid = require('uuid');
|
||||
var shortid = require('shortid');
|
||||
const g = require('strong-globalize')();
|
||||
const util = require('util');
|
||||
const jutil = require('./jutil');
|
||||
const List = require('./list');
|
||||
const DataAccessUtils = require('./model-utils');
|
||||
const Observer = require('./observer');
|
||||
const Hookable = require('./hooks');
|
||||
const validations = require('./validations');
|
||||
const _extend = util._extend;
|
||||
const utils = require('./utils');
|
||||
const fieldsToArray = utils.fieldsToArray;
|
||||
const uuid = require('uuid');
|
||||
const {nanoid} = require('nanoid');
|
||||
|
||||
// Set up an object for quick lookup
|
||||
var BASE_TYPES = {
|
||||
const BASE_TYPES = {
|
||||
'String': true,
|
||||
'Boolean': true,
|
||||
'Number': true,
|
||||
|
@ -71,8 +73,8 @@ function ModelBaseClass(data, options) {
|
|||
* @private
|
||||
*/
|
||||
ModelBaseClass.prototype._initProperties = function(data, options) {
|
||||
var self = this;
|
||||
var ctor = this.constructor;
|
||||
const self = this;
|
||||
const ctor = this.constructor;
|
||||
|
||||
if (typeof data !== 'undefined' && data !== null && data.constructor &&
|
||||
typeof (data.constructor) !== 'function') {
|
||||
|
@ -83,7 +85,7 @@ ModelBaseClass.prototype._initProperties = function(data, options) {
|
|||
// Convert the data to be plain object to avoid pollutions
|
||||
data = data.toObject(false);
|
||||
}
|
||||
var properties = _extend({}, ctor.definition.properties);
|
||||
const properties = _extend({}, ctor.definition.properties);
|
||||
data = data || {};
|
||||
|
||||
if (typeof ctor.applyProperties === 'function') {
|
||||
|
@ -91,9 +93,9 @@ ModelBaseClass.prototype._initProperties = function(data, options) {
|
|||
}
|
||||
|
||||
options = options || {};
|
||||
var applySetters = options.applySetters;
|
||||
var applyDefaultValues = options.applyDefaultValues;
|
||||
var strict = options.strict;
|
||||
const applySetters = options.applySetters;
|
||||
const applyDefaultValues = options.applyDefaultValues;
|
||||
let strict = options.strict;
|
||||
|
||||
if (strict === undefined) {
|
||||
strict = ctor.definition.settings.strict;
|
||||
|
@ -103,7 +105,7 @@ ModelBaseClass.prototype._initProperties = function(data, options) {
|
|||
'{{`Validation Error`}} for unknown properties,', ctor.modelName);
|
||||
}
|
||||
|
||||
var persistUndefinedAsNull = ctor.definition.settings.persistUndefinedAsNull;
|
||||
const persistUndefinedAsNull = ctor.definition.settings.persistUndefinedAsNull;
|
||||
|
||||
if (ctor.hideInternalProperties) {
|
||||
// Object.defineProperty() is expensive. We only try to make the internal
|
||||
|
@ -175,7 +177,7 @@ ModelBaseClass.prototype._initProperties = function(data, options) {
|
|||
this.__cachedRelations = data.__cachedRelations;
|
||||
}
|
||||
|
||||
var keys = Object.keys(data);
|
||||
let keys = Object.keys(data);
|
||||
|
||||
if (Array.isArray(options.fields)) {
|
||||
keys = keys.filter(function(k) {
|
||||
|
@ -183,9 +185,9 @@ ModelBaseClass.prototype._initProperties = function(data, options) {
|
|||
});
|
||||
}
|
||||
|
||||
var size = keys.length;
|
||||
var p, propVal;
|
||||
for (var k = 0; k < size; k++) {
|
||||
let size = keys.length;
|
||||
let p, propVal;
|
||||
for (let k = 0; k < size; k++) {
|
||||
p = keys[k];
|
||||
propVal = data[p];
|
||||
if (typeof propVal === 'function') {
|
||||
|
@ -204,14 +206,14 @@ ModelBaseClass.prototype._initProperties = function(data, options) {
|
|||
self.__data[p] = propVal;
|
||||
}
|
||||
} else if (ctor.relations[p]) {
|
||||
var relationType = ctor.relations[p].type;
|
||||
const relationType = ctor.relations[p].type;
|
||||
|
||||
var modelTo;
|
||||
let modelTo;
|
||||
if (!properties[p]) {
|
||||
modelTo = ctor.relations[p].modelTo || ModelBaseClass;
|
||||
var multiple = ctor.relations[p].multiple;
|
||||
var typeName = multiple ? 'Array' : modelTo.modelName;
|
||||
var propType = multiple ? [modelTo] : modelTo;
|
||||
const multiple = ctor.relations[p].multiple;
|
||||
const typeName = multiple ? 'Array' : modelTo.modelName;
|
||||
const propType = multiple ? [modelTo] : modelTo;
|
||||
properties[p] = {name: typeName, type: propType};
|
||||
/* Issue #1252
|
||||
this.setStrict(false);
|
||||
|
@ -224,7 +226,7 @@ ModelBaseClass.prototype._initProperties = function(data, options) {
|
|||
self.__data[ctor.relations[p].keyFrom] = propVal[ctor.relations[p].keyTo];
|
||||
|
||||
if (ctor.relations[p].options.embedsProperties) {
|
||||
var fields = fieldsToArray(ctor.relations[p].properties,
|
||||
const fields = fieldsToArray(ctor.relations[p].properties,
|
||||
modelTo.definition.properties, modelTo.settings.strict);
|
||||
if (!~fields.indexOf(ctor.relations[p].keyTo)) {
|
||||
fields.push(ctor.relations[p].keyTo);
|
||||
|
@ -249,7 +251,7 @@ ModelBaseClass.prototype._initProperties = function(data, options) {
|
|||
throw new Error(g.f(
|
||||
'Property names containing dot(s) are not supported. ' +
|
||||
'Model: %s, dynamic property: %s',
|
||||
this.constructor.modelName, p
|
||||
this.constructor.modelName, p,
|
||||
));
|
||||
}
|
||||
} else {
|
||||
|
@ -270,14 +272,14 @@ ModelBaseClass.prototype._initProperties = function(data, options) {
|
|||
|
||||
size = keys.length;
|
||||
|
||||
for (k = 0; k < size; k++) {
|
||||
for (let k = 0; k < size; k++) {
|
||||
p = keys[k];
|
||||
propVal = self.__data[p];
|
||||
var type = properties[p].type;
|
||||
const type = properties[p].type;
|
||||
|
||||
// Set default values
|
||||
if (applyDefaultValues && propVal === undefined) {
|
||||
var def = properties[p]['default'];
|
||||
if (applyDefaultValues && propVal === undefined && appliesDefaultsOnWrites(properties[p])) {
|
||||
let def = properties[p]['default'];
|
||||
if (def !== undefined) {
|
||||
if (typeof def === 'function') {
|
||||
if (def === Date) {
|
||||
|
@ -297,9 +299,13 @@ ModelBaseClass.prototype._initProperties = function(data, options) {
|
|||
}
|
||||
}
|
||||
|
||||
if (ignoresMatchedDefault(properties[p]) && properties[p].default === propVal) {
|
||||
delete self.__data[p];
|
||||
}
|
||||
|
||||
// Set default value using a named function
|
||||
if (applyDefaultValues && propVal === undefined) {
|
||||
var defn = properties[p].defaultFn;
|
||||
const defn = properties[p].defaultFn;
|
||||
switch (defn) {
|
||||
case undefined:
|
||||
break;
|
||||
|
@ -316,7 +322,8 @@ ModelBaseClass.prototype._initProperties = function(data, options) {
|
|||
propVal = new Date();
|
||||
break;
|
||||
case 'shortid':
|
||||
propVal = shortid.generate();
|
||||
case 'nanoid':
|
||||
propVal = nanoid(9);
|
||||
break;
|
||||
default:
|
||||
// TODO Support user-provided functions via a registry of functions
|
||||
|
@ -347,6 +354,7 @@ ModelBaseClass.prototype._initProperties = function(data, options) {
|
|||
typeof self.__data[p] === 'object' &&
|
||||
self.__data[p] !== null) {
|
||||
self.__data[p] = new type(self.__data[p]);
|
||||
utils.applyParentProperty(self.__data[p], this);
|
||||
}
|
||||
} else if (type.name === 'Array' || Array.isArray(type)) {
|
||||
if (!(self.__data[p] instanceof List) &&
|
||||
|
@ -360,6 +368,21 @@ ModelBaseClass.prototype._initProperties = function(data, options) {
|
|||
this.trigger('initialize');
|
||||
};
|
||||
|
||||
// Implementation of persistDefaultValues property
|
||||
function ignoresMatchedDefault(property) {
|
||||
if (property && property.persistDefaultValues === false) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// Helper function for determing the applyDefaultOnWrites value of a property
|
||||
function appliesDefaultsOnWrites(property) {
|
||||
if (property && ('applyDefaultOnWrites' in property)) {
|
||||
return property.applyDefaultOnWrites;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Define a property on the model.
|
||||
* @param {String} prop Property name
|
||||
|
@ -379,7 +402,7 @@ ModelBaseClass.defineProperty = function(prop, params) {
|
|||
* @returns {String} Name of property type
|
||||
*/
|
||||
ModelBaseClass.getPropertyType = function(propName) {
|
||||
var prop = this.definition.properties[propName];
|
||||
const prop = this.definition.properties[propName];
|
||||
if (!prop) {
|
||||
// The property is not part of the definition
|
||||
return null;
|
||||
|
@ -418,27 +441,33 @@ ModelBaseClass.toString = function() {
|
|||
* @returns {Object} returns Plain JSON object
|
||||
*/
|
||||
ModelBaseClass.prototype.toObject = function(onlySchema, removeHidden, removeProtected) {
|
||||
if (typeof onlySchema === 'object' && onlySchema != null) {
|
||||
const options = onlySchema;
|
||||
onlySchema = options.onlySchema;
|
||||
removeHidden = options.removeHidden;
|
||||
removeProtected = options.removeProtected;
|
||||
}
|
||||
if (onlySchema === undefined) {
|
||||
onlySchema = true;
|
||||
}
|
||||
var data = {};
|
||||
var self = this;
|
||||
var Model = this.constructor;
|
||||
const data = {};
|
||||
const self = this;
|
||||
const Model = this.constructor;
|
||||
|
||||
// if it is already an Object
|
||||
if (Model === Object) {
|
||||
return self;
|
||||
}
|
||||
|
||||
var strict = this.__strict;
|
||||
var schemaLess = (strict === false) || !onlySchema;
|
||||
var persistUndefinedAsNull = Model.definition.settings.persistUndefinedAsNull;
|
||||
const strict = this.__strict;
|
||||
const schemaLess = (strict === false) || !onlySchema;
|
||||
const persistUndefinedAsNull = Model.definition.settings.persistUndefinedAsNull;
|
||||
|
||||
var props = Model.definition.properties;
|
||||
var keys = Object.keys(props);
|
||||
var propertyName, val;
|
||||
const props = Model.definition.properties;
|
||||
let keys = Object.keys(props);
|
||||
let propertyName, val;
|
||||
|
||||
for (var i = 0; i < keys.length; i++) {
|
||||
for (let i = 0; i < keys.length; i++) {
|
||||
propertyName = keys[i];
|
||||
val = self[propertyName];
|
||||
|
||||
|
@ -474,8 +503,8 @@ ModelBaseClass.prototype.toObject = function(onlySchema, removeHidden, removePro
|
|||
// If the property is not declared in the model definition, no setter will be
|
||||
// triggered to add it to __data
|
||||
keys = Object.keys(self);
|
||||
var size = keys.length;
|
||||
for (i = 0; i < size; i++) {
|
||||
let size = keys.length;
|
||||
for (let i = 0; i < size; i++) {
|
||||
propertyName = keys[i];
|
||||
if (props[propertyName]) {
|
||||
continue;
|
||||
|
@ -509,7 +538,7 @@ ModelBaseClass.prototype.toObject = function(onlySchema, removeHidden, removePro
|
|||
// Now continue to check __data
|
||||
keys = Object.keys(self.__data);
|
||||
size = keys.length;
|
||||
for (i = 0; i < size; i++) {
|
||||
for (let i = 0; i < size; i++) {
|
||||
propertyName = keys[i];
|
||||
if (propertyName.indexOf('__') === 0) {
|
||||
continue;
|
||||
|
@ -521,7 +550,7 @@ ModelBaseClass.prototype.toObject = function(onlySchema, removeHidden, removePro
|
|||
if (removeProtected && Model.isProtectedProperty(propertyName)) {
|
||||
continue;
|
||||
}
|
||||
var ownVal = self[propertyName];
|
||||
const ownVal = self[propertyName];
|
||||
// The ownVal can be a relation function
|
||||
val = (ownVal !== undefined && (typeof ownVal !== 'function')) ? ownVal : self.__data[propertyName];
|
||||
if (typeof val === 'function') {
|
||||
|
@ -542,28 +571,30 @@ ModelBaseClass.prototype.toObject = function(onlySchema, removeHidden, removePro
|
|||
return data;
|
||||
};
|
||||
|
||||
/**
|
||||
* Convert an array of strings into an object as the map
|
||||
* @param {string[]} arr An array of strings
|
||||
*/
|
||||
function asObjectMap(arr) {
|
||||
const obj = {};
|
||||
if (Array.isArray(arr)) {
|
||||
for (let i = 0; i < arr.length; i++) {
|
||||
obj[arr[i]] = true;
|
||||
}
|
||||
return obj;
|
||||
}
|
||||
return arr || obj;
|
||||
}
|
||||
/**
|
||||
* Checks if property is protected.
|
||||
* @param {String} propertyName Property name
|
||||
* @returns {Boolean} true or false if proptected or not.
|
||||
* @returns {Boolean} true or false if protected or not.
|
||||
*/
|
||||
ModelBaseClass.isProtectedProperty = function(propertyName) {
|
||||
var Model = this;
|
||||
var settings = Model.definition && Model.definition.settings;
|
||||
var protectedProperties = settings && (settings.protectedProperties || settings.protected);
|
||||
if (Array.isArray(protectedProperties)) {
|
||||
// Cache the protected properties as an object for quick lookup
|
||||
settings.protectedProperties = {};
|
||||
for (var i = 0; i < protectedProperties.length; i++) {
|
||||
settings.protectedProperties[protectedProperties[i]] = true;
|
||||
}
|
||||
protectedProperties = settings.protectedProperties;
|
||||
}
|
||||
if (protectedProperties) {
|
||||
return protectedProperties[propertyName];
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
const settings = (this.definition && this.definition.settings) || {};
|
||||
const protectedProperties = settings.protectedProperties || settings.protected;
|
||||
settings.protectedProperties = asObjectMap(protectedProperties);
|
||||
return settings.protectedProperties[propertyName];
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -572,22 +603,10 @@ ModelBaseClass.isProtectedProperty = function(propertyName) {
|
|||
* @returns {Boolean} true or false if hidden or not.
|
||||
*/
|
||||
ModelBaseClass.isHiddenProperty = function(propertyName) {
|
||||
var Model = this;
|
||||
var settings = Model.definition && Model.definition.settings;
|
||||
var hiddenProperties = settings && (settings.hiddenProperties || settings.hidden);
|
||||
if (Array.isArray(hiddenProperties)) {
|
||||
// Cache the hidden properties as an object for quick lookup
|
||||
settings.hiddenProperties = {};
|
||||
for (var i = 0; i < hiddenProperties.length; i++) {
|
||||
settings.hiddenProperties[hiddenProperties[i]] = true;
|
||||
}
|
||||
hiddenProperties = settings.hiddenProperties;
|
||||
}
|
||||
if (hiddenProperties) {
|
||||
return hiddenProperties[propertyName];
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
const settings = (this.definition && this.definition.settings) || {};
|
||||
const hiddenProperties = settings.hiddenProperties || settings.hidden;
|
||||
settings.hiddenProperties = asObjectMap(hiddenProperties);
|
||||
return settings.hiddenProperties[propertyName];
|
||||
};
|
||||
|
||||
ModelBaseClass.prototype.toJSON = function() {
|
||||
|
@ -595,7 +614,7 @@ ModelBaseClass.prototype.toJSON = function() {
|
|||
};
|
||||
|
||||
ModelBaseClass.prototype.fromObject = function(obj) {
|
||||
for (var key in obj) {
|
||||
for (const key in obj) {
|
||||
this[key] = obj[key];
|
||||
}
|
||||
};
|
||||
|
@ -606,8 +625,8 @@ ModelBaseClass.prototype.fromObject = function(obj) {
|
|||
* initial state.
|
||||
*/
|
||||
ModelBaseClass.prototype.reset = function() {
|
||||
var obj = this;
|
||||
for (var k in obj) {
|
||||
const obj = this;
|
||||
for (const k in obj) {
|
||||
if (k !== 'id' && !obj.constructor.dataSource.definitions[obj.constructor.modelName].properties[k]) {
|
||||
delete obj[k];
|
||||
}
|
||||
|
@ -617,11 +636,11 @@ ModelBaseClass.prototype.reset = function() {
|
|||
// Node v0.11+ allows custom inspect functions to return an object
|
||||
// instead of string. That way options like `showHidden` and `colors`
|
||||
// can be preserved.
|
||||
var versionParts = process.versions && process.versions.node ?
|
||||
const versionParts = process.versions && process.versions.node ?
|
||||
process.versions.node.split(/\./g).map(function(v) { return +v; }) :
|
||||
[1, 0, 0]; // browserify ships 1.0-compatible version of util.inspect
|
||||
|
||||
var INSPECT_SUPPORTS_OBJECT_RETVAL =
|
||||
const INSPECT_SUPPORTS_OBJECT_RETVAL =
|
||||
versionParts[0] > 0 ||
|
||||
versionParts[1] > 11 ||
|
||||
(versionParts[0] === 11 && versionParts[1] >= 14);
|
||||
|
@ -639,6 +658,13 @@ ModelBaseClass.prototype.inspect = function(depth) {
|
|||
});
|
||||
};
|
||||
|
||||
if (util.inspect.custom) {
|
||||
// Node.js 12+ no longer recognizes "inspect" method,
|
||||
// it uses "inspect.custom" symbol as the key instead
|
||||
// TODO(semver-major) always use the symbol key only (requires Node.js 8+).
|
||||
ModelBaseClass.prototype[util.inspect.custom] = ModelBaseClass.prototype.inspect;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {String} anotherClass could be string or class. Name of the class or the class itself
|
||||
|
@ -650,8 +676,8 @@ ModelBaseClass.mixin = function(anotherClass, options) {
|
|||
this.modelBuilder.mixins.applyMixin(this, anotherClass, options);
|
||||
} else {
|
||||
if (anotherClass.prototype instanceof ModelBaseClass) {
|
||||
var props = anotherClass.definition.properties;
|
||||
for (var i in props) {
|
||||
const props = anotherClass.definition.properties;
|
||||
for (const i in props) {
|
||||
if (this.definition.properties[i]) {
|
||||
continue;
|
||||
}
|
||||
|
@ -782,7 +808,7 @@ ModelBaseClass.getMergePolicy = function(options) {
|
|||
// + fix for description arrays that should not be merged
|
||||
// + fix for relations that should patch matching relations
|
||||
// + ranking of ACLs
|
||||
var mergePolicy = {
|
||||
let mergePolicy = {
|
||||
description: {replace: true}, // string or array
|
||||
properties: {patch: true}, // object
|
||||
hidden: {replace: false}, // array
|
||||
|
@ -791,7 +817,7 @@ ModelBaseClass.getMergePolicy = function(options) {
|
|||
acls: {rank: true}, // array
|
||||
};
|
||||
|
||||
var config = (options || {}).configureModelMerge;
|
||||
const config = (options || {}).configureModelMerge;
|
||||
|
||||
if (config === true) {
|
||||
// NOTE: recommended merge policy from datasource-juggler v3.6.2
|
||||
|
@ -834,13 +860,15 @@ ModelBaseClass.getMergePolicy = function(options) {
|
|||
*/
|
||||
|
||||
ModelBaseClass.getUpdateOnlyProperties = function() {
|
||||
var Model = this;
|
||||
const props = this.definition.properties;
|
||||
return Object.keys(props).filter(key => props[key].updateOnly);
|
||||
};
|
||||
|
||||
// Mix in utils
|
||||
jutil.mixin(ModelBaseClass, DataAccessUtils);
|
||||
|
||||
// Mixin observer
|
||||
jutil.mixin(ModelBaseClass, require('./observer'));
|
||||
jutil.mixin(ModelBaseClass, Observer);
|
||||
|
||||
jutil.mixin(ModelBaseClass, Hookable);
|
||||
jutil.mixin(ModelBaseClass, validations.Validatable);
|
||||
|
|
|
@ -1,11 +1,13 @@
|
|||
// Copyright IBM Corp. 2015,2016. All Rights Reserved.
|
||||
// Copyright IBM Corp. 2015,2019. All Rights Reserved.
|
||||
// Node module: loopback-datasource-juggler
|
||||
// This file is licensed under the MIT License.
|
||||
// License text available at https://opensource.org/licenses/MIT
|
||||
|
||||
'use strict';
|
||||
|
||||
var async = require('async');
|
||||
var utils = require('./utils');
|
||||
const async = require('async');
|
||||
const utils = require('./utils');
|
||||
const debug = require('debug')('loopback:observer');
|
||||
|
||||
module.exports = ObserverMixin;
|
||||
|
||||
|
@ -74,7 +76,7 @@ ObserverMixin.observe = function(operation, listener) {
|
|||
ObserverMixin.removeObserver = function(operation, listener) {
|
||||
if (!(this._observers && this._observers[operation])) return;
|
||||
|
||||
var index = this._observers[operation].indexOf(listener);
|
||||
const index = this._observers[operation].indexOf(listener);
|
||||
if (index !== -1) {
|
||||
return this._observers[operation].splice(index, 1);
|
||||
}
|
||||
|
@ -127,7 +129,7 @@ ObserverMixin.clearObservers = function(operation) {
|
|||
* have finished.
|
||||
*/
|
||||
ObserverMixin.notifyObserversOf = function(operation, context, callback) {
|
||||
var self = this;
|
||||
const self = this;
|
||||
if (!callback) callback = utils.createPromiseCallback();
|
||||
|
||||
function createNotifier(op) {
|
||||
|
@ -141,14 +143,14 @@ ObserverMixin.notifyObserversOf = function(operation, context, callback) {
|
|||
}
|
||||
|
||||
if (Array.isArray(operation)) {
|
||||
var tasks = [];
|
||||
for (var i = 0, n = operation.length; i < n; i++) {
|
||||
const tasks = [];
|
||||
for (let i = 0, n = operation.length; i < n; i++) {
|
||||
tasks.push(createNotifier(operation[i]));
|
||||
}
|
||||
return async.waterfall(tasks, callback);
|
||||
}
|
||||
|
||||
var observers = this._observers && this._observers[operation];
|
||||
const observers = this._observers && this._observers[operation];
|
||||
|
||||
this._notifyBaseObservers(operation, context, function doNotify(err) {
|
||||
if (err) return callback(err, context);
|
||||
|
@ -157,15 +159,15 @@ ObserverMixin.notifyObserversOf = function(operation, context, callback) {
|
|||
async.eachSeries(
|
||||
observers,
|
||||
function notifySingleObserver(fn, next) {
|
||||
var retval = fn(context, next);
|
||||
const retval = fn(context, next);
|
||||
if (retval && typeof retval.then === 'function') {
|
||||
retval.then(
|
||||
function() { next(); return null; },
|
||||
next // error handler
|
||||
next, // error handler
|
||||
);
|
||||
}
|
||||
},
|
||||
function(err) { callback(err, context); }
|
||||
function(err) { callback(err, context); },
|
||||
);
|
||||
});
|
||||
return callback.promise;
|
||||
|
@ -217,7 +219,7 @@ ObserverMixin._notifyBaseObservers = function(operation, context, callback) {
|
|||
* @returns {*}
|
||||
*/
|
||||
ObserverMixin.notifyObserversAround = function(operation, context, fn, callback) {
|
||||
var self = this;
|
||||
const self = this;
|
||||
context = context || {};
|
||||
// Add callback to the context object so that an observer can skip other
|
||||
// ones by calling the callback function directly and not calling next
|
||||
|
@ -230,23 +232,40 @@ ObserverMixin.notifyObserversAround = function(operation, context, fn, callback)
|
|||
if (err) return callback(err);
|
||||
|
||||
function cbForWork(err) {
|
||||
var args = [].slice.call(arguments, 0);
|
||||
if (err) return callback.apply(null, args);
|
||||
const args = [].slice.call(arguments, 0);
|
||||
if (err) {
|
||||
// call observer in case of error to hook response
|
||||
context.error = err;
|
||||
self.notifyObserversOf('after ' + operation + ' error', context,
|
||||
function(_err, context) {
|
||||
if (_err && err) {
|
||||
debug(
|
||||
'Operation %j failed and "after %s error" hook returned an error too. ' +
|
||||
'Calling back with the hook error only.' +
|
||||
'\nOriginal error: %s\nHook error: %s\n',
|
||||
err.stack || err,
|
||||
_err.stack || _err,
|
||||
);
|
||||
}
|
||||
callback.call(null, _err || err, context);
|
||||
});
|
||||
return;
|
||||
}
|
||||
// Find the list of params from the callback in addition to err
|
||||
var returnedArgs = args.slice(1);
|
||||
const returnedArgs = args.slice(1);
|
||||
// Set up the array of results
|
||||
context.results = returnedArgs;
|
||||
// Notify after observers
|
||||
self.notifyObserversOf('after ' + operation, context,
|
||||
function(err, context) {
|
||||
if (err) return callback(err, context);
|
||||
var results = returnedArgs;
|
||||
let results = returnedArgs;
|
||||
if (context && Array.isArray(context.results)) {
|
||||
// Pickup the results from context
|
||||
results = context.results;
|
||||
}
|
||||
// Build the list of params for final callback
|
||||
var args = [err].concat(results);
|
||||
const args = [err].concat(results);
|
||||
callback.apply(null, args);
|
||||
});
|
||||
}
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -1,14 +1,15 @@
|
|||
// Copyright IBM Corp. 2013,2016. All Rights Reserved.
|
||||
// Copyright IBM Corp. 2013,2019. All Rights Reserved.
|
||||
// Node module: loopback-datasource-juggler
|
||||
// This file is licensed under the MIT License.
|
||||
// License text available at https://opensource.org/licenses/MIT
|
||||
|
||||
'use strict';
|
||||
|
||||
/*!
|
||||
* Dependencies
|
||||
*/
|
||||
var relation = require('./relation-definition');
|
||||
var RelationDefinition = relation.RelationDefinition;
|
||||
const relation = require('./relation-definition');
|
||||
const RelationDefinition = relation.RelationDefinition;
|
||||
|
||||
module.exports = RelationMixin;
|
||||
|
||||
|
|
142
lib/scope.js
142
lib/scope.js
|
@ -1,20 +1,21 @@
|
|||
// Copyright IBM Corp. 2013,2016. All Rights Reserved.
|
||||
// Copyright IBM Corp. 2013,2019. All Rights Reserved.
|
||||
// Node module: loopback-datasource-juggler
|
||||
// This file is licensed under the MIT License.
|
||||
// License text available at https://opensource.org/licenses/MIT
|
||||
|
||||
'use strict';
|
||||
|
||||
var _ = require('lodash');
|
||||
var i8n = require('inflection');
|
||||
var g = require('strong-globalize')();
|
||||
var utils = require('./utils');
|
||||
var defineCachedRelations = utils.defineCachedRelations;
|
||||
var setScopeValuesFromWhere = utils.setScopeValuesFromWhere;
|
||||
var mergeQuery = utils.mergeQuery;
|
||||
var DefaultModelBaseClass = require('./model.js');
|
||||
var collectTargetIds = utils.collectTargetIds;
|
||||
var idName = utils.idName;
|
||||
var deprecated = require('depd')('loopback-datasource-juggler');
|
||||
const _ = require('lodash');
|
||||
const i8n = require('inflection');
|
||||
const g = require('strong-globalize')();
|
||||
const utils = require('./utils');
|
||||
const defineCachedRelations = utils.defineCachedRelations;
|
||||
const setScopeValuesFromWhere = utils.setScopeValuesFromWhere;
|
||||
const mergeQuery = utils.mergeQuery;
|
||||
const DefaultModelBaseClass = require('./model.js');
|
||||
const collectTargetIds = utils.collectTargetIds;
|
||||
const idName = utils.idName;
|
||||
const deprecated = require('depd')('loopback-datasource-juggler');
|
||||
|
||||
/**
|
||||
* Module exports
|
||||
|
@ -32,14 +33,14 @@ function ScopeDefinition(definition) {
|
|||
}
|
||||
|
||||
ScopeDefinition.prototype.targetModel = function(receiver) {
|
||||
var modelTo;
|
||||
let modelTo;
|
||||
if (typeof this.options.modelTo === 'function') {
|
||||
modelTo = this.options.modelTo.call(this, receiver) || this.modelTo;
|
||||
} else {
|
||||
modelTo = this.modelTo;
|
||||
}
|
||||
if (!(modelTo.prototype instanceof DefaultModelBaseClass)) {
|
||||
var msg = 'Invalid target model for scope `';
|
||||
let msg = 'Invalid target model for scope `';
|
||||
msg += (this.isStatic ? this.modelFrom : this.modelFrom.constructor).modelName;
|
||||
msg += this.isStatic ? '.' : '.prototype.';
|
||||
msg += this.name + '`.';
|
||||
|
@ -58,12 +59,12 @@ ScopeDefinition.prototype.targetModel = function(receiver) {
|
|||
* @returns {*}
|
||||
*/
|
||||
ScopeDefinition.prototype.related = function(receiver, scopeParams, condOrRefresh, options, cb) {
|
||||
var name = this.name;
|
||||
var self = receiver;
|
||||
const name = this.name;
|
||||
const self = receiver;
|
||||
|
||||
var actualCond = {};
|
||||
var actualRefresh = false;
|
||||
var saveOnCache = receiver instanceof DefaultModelBaseClass;
|
||||
let actualCond = {};
|
||||
let actualRefresh = false;
|
||||
let saveOnCache = receiver instanceof DefaultModelBaseClass;
|
||||
if (typeof condOrRefresh === 'function' &&
|
||||
options === undefined && cb === undefined) {
|
||||
// related(receiver, scopeParams, cb)
|
||||
|
@ -90,21 +91,22 @@ ScopeDefinition.prototype.related = function(receiver, scopeParams, condOrRefres
|
|||
actualRefresh;
|
||||
if (refreshIsNeeded) {
|
||||
// It either doesn't hit the cache or refresh is required
|
||||
var params = mergeQuery(actualCond, scopeParams, {nestedInclude: true});
|
||||
var targetModel = this.targetModel(receiver);
|
||||
const params = mergeQuery(actualCond, scopeParams, {nestedInclude: true});
|
||||
const targetModel = this.targetModel(receiver);
|
||||
|
||||
// If there is a through model
|
||||
// run another query to apply filter on relatedModel(targetModel)
|
||||
// see github.com/strongloop/loopback-datasource-juggler/issues/166
|
||||
var scopeOnRelatedModel = params.collect &&
|
||||
const scopeOnRelatedModel = params.collect &&
|
||||
params.include.scope !== null &&
|
||||
typeof params.include.scope === 'object';
|
||||
let filter, queryRelated;
|
||||
if (scopeOnRelatedModel) {
|
||||
var filter = params.include;
|
||||
filter = params.include;
|
||||
// The filter applied on relatedModel
|
||||
var queryRelated = filter.scope;
|
||||
queryRelated = filter.scope;
|
||||
delete params.include.scope;
|
||||
};
|
||||
}
|
||||
|
||||
targetModel.find(params, options, function(err, data) {
|
||||
if (!err && saveOnCache) {
|
||||
|
@ -113,18 +115,18 @@ ScopeDefinition.prototype.related = function(receiver, scopeParams, condOrRefres
|
|||
}
|
||||
|
||||
if (scopeOnRelatedModel === true) {
|
||||
var relatedModel = targetModel.relations[filter.relation].modelTo;
|
||||
var IdKey = idName(relatedModel);
|
||||
const relatedModel = targetModel.relations[filter.relation].modelTo;
|
||||
const IdKey = idName(relatedModel);
|
||||
|
||||
// return {inq: [1,2,3]}}
|
||||
var smartMerge = function(idCollection, qWhere) {
|
||||
const smartMerge = function(idCollection, qWhere) {
|
||||
if (!qWhere[IdKey]) return idCollection;
|
||||
var merged = {};
|
||||
let merged = {};
|
||||
|
||||
var idsA = idCollection.inq;
|
||||
var idsB = qWhere[IdKey].inq ? qWhere[IdKey].inq : [qWhere[IdKey]];
|
||||
const idsA = idCollection.inq;
|
||||
const idsB = qWhere[IdKey].inq ? qWhere[IdKey].inq : [qWhere[IdKey]];
|
||||
|
||||
var intersect = _.intersectionWith(idsA, idsB, _.isEqual);
|
||||
const intersect = _.intersectionWith(idsA, idsB, _.isEqual);
|
||||
if (intersect.length === 1) merged = intersect[0];
|
||||
if (intersect.length > 1) merged = {inq: intersect};
|
||||
|
||||
|
@ -133,7 +135,7 @@ ScopeDefinition.prototype.related = function(receiver, scopeParams, condOrRefres
|
|||
|
||||
if (queryRelated.where !== undefined) {
|
||||
// Merge queryRelated filter and targetId filter
|
||||
var IdKeyCondition = {};
|
||||
const IdKeyCondition = {};
|
||||
IdKeyCondition[IdKey] = smartMerge(collectTargetIds(data, IdKey),
|
||||
queryRelated.where);
|
||||
|
||||
|
@ -141,7 +143,7 @@ ScopeDefinition.prototype.related = function(receiver, scopeParams, condOrRefres
|
|||
// return empty result
|
||||
if (_.isObject(IdKeyCondition[IdKey]) && _.isEmpty(IdKeyCondition[IdKey])) return cb(null, []);
|
||||
|
||||
var mergedWhere = {
|
||||
const mergedWhere = {
|
||||
and: [
|
||||
IdKeyCondition,
|
||||
_.omit(queryRelated.where, IdKey),
|
||||
|
@ -201,8 +203,8 @@ function defineScope(cls, targetClass, name, params, methods, options) {
|
|||
|
||||
options = options || {};
|
||||
// Check if the cls is the class itself or its prototype
|
||||
var isStatic = (typeof cls === 'function') || options.isStatic || false;
|
||||
var definition = new ScopeDefinition({
|
||||
const isStatic = (typeof cls === 'function') || options.isStatic || false;
|
||||
const definition = new ScopeDefinition({
|
||||
isStatic: isStatic,
|
||||
modelFrom: cls,
|
||||
modelTo: targetClass,
|
||||
|
@ -236,10 +238,10 @@ function defineScope(cls, targetClass, name, params, methods, options) {
|
|||
*
|
||||
*/
|
||||
get: function() {
|
||||
var targetModel = definition.targetModel(this);
|
||||
var self = this;
|
||||
const targetModel = definition.targetModel(this);
|
||||
const self = this;
|
||||
|
||||
var f = function(condOrRefresh, options, cb) {
|
||||
const f = function(condOrRefresh, options, cb) {
|
||||
if (arguments.length === 0) {
|
||||
if (typeof f.value === 'function') {
|
||||
return f.value(self);
|
||||
|
@ -312,7 +314,7 @@ function defineScope(cls, targetClass, name, params, methods, options) {
|
|||
f.findOne = findOne;
|
||||
f.count = count;
|
||||
|
||||
for (var i in definition.methods) {
|
||||
for (const i in definition.methods) {
|
||||
f[i] = definition.methods[i].bind(self);
|
||||
}
|
||||
|
||||
|
@ -336,52 +338,52 @@ function defineScope(cls, targetClass, name, params, methods, options) {
|
|||
});
|
||||
|
||||
// Wrap the property into a function for remoting
|
||||
var fn = function() {
|
||||
const fn = function() {
|
||||
// primaryObject.scopeName, such as user.accounts
|
||||
var f = this[name];
|
||||
const f = this[name];
|
||||
// set receiver to be the scope property whose value is a function
|
||||
f.apply(this[name], arguments);
|
||||
};
|
||||
|
||||
cls['__get__' + name] = fn;
|
||||
|
||||
var fnCreate = function() {
|
||||
var f = this[name].create;
|
||||
const fnCreate = function() {
|
||||
const f = this[name].create;
|
||||
f.apply(this[name], arguments);
|
||||
};
|
||||
|
||||
cls['__create__' + name] = fnCreate;
|
||||
|
||||
var fnDelete = function() {
|
||||
var f = this[name].destroyAll;
|
||||
const fnDelete = function() {
|
||||
const f = this[name].destroyAll;
|
||||
f.apply(this[name], arguments);
|
||||
};
|
||||
|
||||
cls['__delete__' + name] = fnDelete;
|
||||
|
||||
var fnUpdate = function() {
|
||||
var f = this[name].updateAll;
|
||||
const fnUpdate = function() {
|
||||
const f = this[name].updateAll;
|
||||
f.apply(this[name], arguments);
|
||||
};
|
||||
|
||||
cls['__update__' + name] = fnUpdate;
|
||||
|
||||
var fnFindById = function(cb) {
|
||||
var f = this[name].findById;
|
||||
const fnFindById = function(cb) {
|
||||
const f = this[name].findById;
|
||||
f.apply(this[name], arguments);
|
||||
};
|
||||
|
||||
cls['__findById__' + name] = fnFindById;
|
||||
|
||||
var fnFindOne = function(cb) {
|
||||
var f = this[name].findOne;
|
||||
const fnFindOne = function(cb) {
|
||||
const f = this[name].findOne;
|
||||
f.apply(this[name], arguments);
|
||||
};
|
||||
|
||||
cls['__findOne__' + name] = fnFindOne;
|
||||
|
||||
var fnCount = function(cb) {
|
||||
var f = this[name].count;
|
||||
const fnCount = function(cb) {
|
||||
const f = this[name].count;
|
||||
f.apply(this[name], arguments);
|
||||
};
|
||||
|
||||
|
@ -391,8 +393,8 @@ function defineScope(cls, targetClass, name, params, methods, options) {
|
|||
function build(data) {
|
||||
data = data || {};
|
||||
// Find all fixed property values for the scope
|
||||
var targetModel = definition.targetModel(this._receiver);
|
||||
var where = (this._scope && this._scope.where) || {};
|
||||
const targetModel = definition.targetModel(this._receiver);
|
||||
const where = (this._scope && this._scope.where) || {};
|
||||
setScopeValuesFromWhere(data, where, targetModel);
|
||||
return new targetModel(data);
|
||||
}
|
||||
|
@ -430,9 +432,9 @@ function defineScope(cls, targetClass, name, params, methods, options) {
|
|||
}
|
||||
options = options || {};
|
||||
|
||||
var targetModel = definition.targetModel(this._receiver);
|
||||
var scoped = (this._scope && this._scope.where) || {};
|
||||
var filter = mergeQuery({where: scoped}, {where: where || {}});
|
||||
const targetModel = definition.targetModel(this._receiver);
|
||||
const scoped = (this._scope && this._scope.where) || {};
|
||||
const filter = mergeQuery({where: scoped}, {where: where || {}});
|
||||
return targetModel.destroyAll(filter.where, options, cb);
|
||||
}
|
||||
|
||||
|
@ -450,9 +452,9 @@ function defineScope(cls, targetClass, name, params, methods, options) {
|
|||
options = {};
|
||||
}
|
||||
options = options || {};
|
||||
var targetModel = definition.targetModel(this._receiver);
|
||||
var scoped = (this._scope && this._scope.where) || {};
|
||||
var filter = mergeQuery({where: scoped}, {where: where || {}});
|
||||
const targetModel = definition.targetModel(this._receiver);
|
||||
const scoped = (this._scope && this._scope.where) || {};
|
||||
const filter = mergeQuery({where: scoped}, {where: where || {}});
|
||||
return targetModel.updateAll(filter.where, data, options, cb);
|
||||
}
|
||||
|
||||
|
@ -478,9 +480,9 @@ function defineScope(cls, targetClass, name, params, methods, options) {
|
|||
|
||||
options = options || {};
|
||||
filter = filter || {};
|
||||
var targetModel = definition.targetModel(this._receiver);
|
||||
var idName = targetModel.definition.idName();
|
||||
var query = {where: {}};
|
||||
const targetModel = definition.targetModel(this._receiver);
|
||||
const idName = targetModel.definition.idName();
|
||||
let query = {where: {}};
|
||||
query.where[idName] = id;
|
||||
query = mergeQuery(query, filter);
|
||||
return this.findOne(query, options, cb);
|
||||
|
@ -498,8 +500,8 @@ function defineScope(cls, targetClass, name, params, methods, options) {
|
|||
options = {};
|
||||
}
|
||||
options = options || {};
|
||||
var targetModel = definition.targetModel(this._receiver);
|
||||
var scoped = (this._scope && this._scope.where) || {};
|
||||
const targetModel = definition.targetModel(this._receiver);
|
||||
const scoped = (this._scope && this._scope.where) || {};
|
||||
filter = mergeQuery({where: scoped}, filter || {});
|
||||
return targetModel.findOne(filter, options, cb);
|
||||
}
|
||||
|
@ -516,9 +518,9 @@ function defineScope(cls, targetClass, name, params, methods, options) {
|
|||
}
|
||||
options = options || {};
|
||||
|
||||
var targetModel = definition.targetModel(this._receiver);
|
||||
var scoped = (this._scope && this._scope.where) || {};
|
||||
var filter = mergeQuery({where: scoped}, {where: where || {}});
|
||||
const targetModel = definition.targetModel(this._receiver);
|
||||
const scoped = (this._scope && this._scope.where) || {};
|
||||
const filter = mergeQuery({where: scoped}, {where: where || {}});
|
||||
return targetModel.count(filter.where, options, cb);
|
||||
}
|
||||
|
||||
|
|
|
@ -1,17 +1,18 @@
|
|||
// Copyright IBM Corp. 2015,2016. All Rights Reserved.
|
||||
// Copyright IBM Corp. 2015,2019. All Rights Reserved.
|
||||
// Node module: loopback-datasource-juggler
|
||||
// This file is licensed under the MIT License.
|
||||
// License text available at https://opensource.org/licenses/MIT
|
||||
|
||||
'use strict';
|
||||
|
||||
var g = require('strong-globalize')();
|
||||
var debug = require('debug')('loopback:connector:transaction');
|
||||
var uuid = require('uuid');
|
||||
var utils = require('./utils');
|
||||
var jutil = require('./jutil');
|
||||
var ObserverMixin = require('./observer');
|
||||
const g = require('strong-globalize')();
|
||||
const debug = require('debug')('loopback:connector:transaction');
|
||||
const uuid = require('uuid');
|
||||
const utils = require('./utils');
|
||||
const jutil = require('./jutil');
|
||||
const ObserverMixin = require('./observer');
|
||||
|
||||
var Transaction = require('loopback-connector').Transaction;
|
||||
const Transaction = require('loopback-connector').Transaction;
|
||||
|
||||
module.exports = TransactionMixin;
|
||||
|
||||
|
@ -73,7 +74,7 @@ function TransactionMixin() {
|
|||
TransactionMixin.beginTransaction = function(options, cb) {
|
||||
cb = cb || utils.createPromiseCallback();
|
||||
if (Transaction) {
|
||||
var connector = this.getConnector();
|
||||
const connector = this.getConnector();
|
||||
Transaction.begin(connector, options, function(err, transaction) {
|
||||
if (err) return cb(err);
|
||||
// NOTE(lehni) As part of the process of moving the handling of
|
||||
|
@ -87,7 +88,7 @@ TransactionMixin.beginTransaction = function(options, cb) {
|
|||
}
|
||||
if (options.timeout && !transaction.timeout) {
|
||||
transaction.timeout = setTimeout(function() {
|
||||
var context = {
|
||||
const context = {
|
||||
transaction: transaction,
|
||||
operation: 'timeout',
|
||||
};
|
||||
|
@ -105,7 +106,7 @@ TransactionMixin.beginTransaction = function(options, cb) {
|
|||
});
|
||||
} else {
|
||||
process.nextTick(function() {
|
||||
var err = new Error(g.f('{{Transaction}} is not supported'));
|
||||
const err = new Error(g.f('{{Transaction}} is not supported'));
|
||||
cb(err);
|
||||
});
|
||||
}
|
||||
|
@ -136,7 +137,7 @@ if (Transaction) {
|
|||
Transaction.prototype.commit = function(cb) {
|
||||
cb = cb || utils.createPromiseCallback();
|
||||
if (this.ensureActive(cb)) {
|
||||
var context = {
|
||||
const context = {
|
||||
transaction: this,
|
||||
operation: 'commit',
|
||||
};
|
||||
|
@ -149,8 +150,7 @@ if (Transaction) {
|
|||
// The connection should have been released back the pool
|
||||
this.connection = null;
|
||||
cb(err);
|
||||
}
|
||||
);
|
||||
});
|
||||
}
|
||||
return cb.promise;
|
||||
};
|
||||
|
@ -176,7 +176,7 @@ if (Transaction) {
|
|||
Transaction.prototype.rollback = function(cb) {
|
||||
cb = cb || utils.createPromiseCallback();
|
||||
if (this.ensureActive(cb)) {
|
||||
var context = {
|
||||
const context = {
|
||||
transaction: this,
|
||||
operation: 'rollback',
|
||||
};
|
||||
|
@ -189,8 +189,7 @@ if (Transaction) {
|
|||
// The connection should have been released back the pool
|
||||
this.connection = null;
|
||||
cb(err);
|
||||
}
|
||||
);
|
||||
});
|
||||
}
|
||||
return cb.promise;
|
||||
};
|
||||
|
|
13
lib/types.js
13
lib/types.js
|
@ -1,10 +1,11 @@
|
|||
// Copyright IBM Corp. 2013,2016. All Rights Reserved.
|
||||
// Copyright IBM Corp. 2013,2019. All Rights Reserved.
|
||||
// Node module: loopback-datasource-juggler
|
||||
// This file is licensed under the MIT License.
|
||||
// License text available at https://opensource.org/licenses/MIT
|
||||
|
||||
'use strict';
|
||||
|
||||
var Types = {};
|
||||
const Types = {};
|
||||
/**
|
||||
* Schema types
|
||||
*/
|
||||
|
@ -40,10 +41,10 @@ Types.Any.prototype.toObject = Types.Any.prototype.toJSON = function() {
|
|||
};
|
||||
|
||||
module.exports = function(modelTypes) {
|
||||
var DateString = require('./date-string');
|
||||
var GeoPoint = require('./geo').GeoPoint;
|
||||
const DateString = require('./date-string');
|
||||
const GeoPoint = require('./geo').GeoPoint;
|
||||
|
||||
for (var t in Types) {
|
||||
for (const t in Types) {
|
||||
modelTypes[t] = Types[t];
|
||||
}
|
||||
|
||||
|
@ -51,7 +52,7 @@ module.exports = function(modelTypes) {
|
|||
modelTypes.registerType = function(type, names) {
|
||||
names = names || [];
|
||||
names = names.concat([type.name]);
|
||||
for (var n = 0; n < names.length; n++) {
|
||||
for (let n = 0; n < names.length; n++) {
|
||||
this.schemaTypes[names[n].toLowerCase()] = type;
|
||||
}
|
||||
};
|
||||
|
|
387
lib/utils.js
387
lib/utils.js
|
@ -1,13 +1,14 @@
|
|||
// Copyright IBM Corp. 2012,2016. All Rights Reserved.
|
||||
// Copyright IBM Corp. 2012,2019. All Rights Reserved.
|
||||
// Node module: loopback-datasource-juggler
|
||||
// This file is licensed under the MIT License.
|
||||
// License text available at https://opensource.org/licenses/MIT
|
||||
|
||||
'use strict';
|
||||
|
||||
exports.safeRequire = safeRequire;
|
||||
exports.fieldsToArray = fieldsToArray;
|
||||
exports.selectFields = selectFields;
|
||||
exports.removeUndefined = removeUndefined;
|
||||
exports.sanitizeQuery = sanitizeQuery;
|
||||
exports.parseSettings = parseSettings;
|
||||
exports.mergeSettings = exports.deepMerge = deepMerge;
|
||||
exports.deepMergeProperty = deepMergeProperty;
|
||||
|
@ -26,11 +27,27 @@ exports.findIndexOf = findIndexOf;
|
|||
exports.collectTargetIds = collectTargetIds;
|
||||
exports.idName = idName;
|
||||
exports.rankArrayElements = rankArrayElements;
|
||||
exports.idsHaveDuplicates = idsHaveDuplicates;
|
||||
exports.isClass = isClass;
|
||||
exports.escapeRegExp = escapeRegExp;
|
||||
exports.applyParentProperty = applyParentProperty;
|
||||
|
||||
var g = require('strong-globalize')();
|
||||
var traverse = require('traverse');
|
||||
var assert = require('assert');
|
||||
var Promise = require('bluebird');
|
||||
const g = require('strong-globalize')();
|
||||
const traverse = require('traverse');
|
||||
const assert = require('assert');
|
||||
const debug = require('debug')('loopback:juggler:utils');
|
||||
|
||||
/**
|
||||
* The name of the property in modelBuilder settings that will enable the child parent reference functionality
|
||||
* @type {string}
|
||||
*/
|
||||
const BUILDER_PARENT_SETTING = 'parentRef';
|
||||
|
||||
/**
|
||||
* The property name that should be defined on each child instance if parent feature flag enabled
|
||||
* @type {string}
|
||||
*/
|
||||
const PARENT_PROPERTY_NAME = '__parent';
|
||||
|
||||
function safeRequire(module) {
|
||||
try {
|
||||
|
@ -51,19 +68,19 @@ function safeRequire(module) {
|
|||
* @param {Object} The where clause
|
||||
*/
|
||||
function setScopeValuesFromWhere(data, where, targetModel) {
|
||||
for (var i in where) {
|
||||
for (const i in where) {
|
||||
if (i === 'and') {
|
||||
// Find fixed property values from each subclauses
|
||||
for (var w = 0, n = where[i].length; w < n; w++) {
|
||||
for (let w = 0, n = where[i].length; w < n; w++) {
|
||||
setScopeValuesFromWhere(data, where[i][w], targetModel);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
var prop = targetModel.definition.properties[i];
|
||||
const prop = targetModel.definition.properties[i];
|
||||
if (prop) {
|
||||
var val = where[i];
|
||||
const val = where[i];
|
||||
if (typeof val !== 'object' || val instanceof prop.type ||
|
||||
prop.type.name === 'ObjectID' || // MongoDB key
|
||||
prop.type.name === 'ObjectID' || // MongoDB key
|
||||
prop.type.name === 'uuidFromString') { // C*
|
||||
// Only pick the {propertyName: propertyValue}
|
||||
data[i] = where[i];
|
||||
|
@ -81,26 +98,26 @@ function setScopeValuesFromWhere(data, where, targetModel) {
|
|||
* @returns {Object}
|
||||
*/
|
||||
function mergeIncludes(destination, source) {
|
||||
var destArray = convertToArray(destination);
|
||||
var sourceArray = convertToArray(source);
|
||||
const destArray = convertToArray(destination);
|
||||
const sourceArray = convertToArray(source);
|
||||
if (destArray.length === 0) {
|
||||
return sourceArray;
|
||||
}
|
||||
if (sourceArray.length === 0) {
|
||||
return destArray;
|
||||
}
|
||||
var relationNames = [];
|
||||
var resultArray = [];
|
||||
for (var j in sourceArray) {
|
||||
var sourceEntry = sourceArray[j];
|
||||
var sourceEntryRelationName = (typeof (sourceEntry.rel || sourceEntry.relation) === 'string') ?
|
||||
const relationNames = [];
|
||||
const resultArray = [];
|
||||
for (const j in sourceArray) {
|
||||
const sourceEntry = sourceArray[j];
|
||||
const sourceEntryRelationName = (typeof (sourceEntry.rel || sourceEntry.relation) === 'string') ?
|
||||
sourceEntry.relation : Object.keys(sourceEntry)[0];
|
||||
relationNames.push(sourceEntryRelationName);
|
||||
resultArray.push(sourceEntry);
|
||||
}
|
||||
for (var i in destArray) {
|
||||
var destEntry = destArray[i];
|
||||
var destEntryRelationName = (typeof (destEntry.rel || destEntry.relation) === 'string') ?
|
||||
for (const i in destArray) {
|
||||
const destEntry = destArray[i];
|
||||
const destEntryRelationName = (typeof (destEntry.rel || destEntry.relation) === 'string') ?
|
||||
destEntry.relation : Object.keys(destEntry)[0];
|
||||
if (relationNames.indexOf(destEntryRelationName) === -1) {
|
||||
resultArray.push(destEntry);
|
||||
|
@ -128,17 +145,17 @@ function convertToArray(include) {
|
|||
return [include];
|
||||
}
|
||||
// Build an array of key/value pairs
|
||||
var newInclude = [];
|
||||
for (var key in include) {
|
||||
const newInclude = [];
|
||||
for (const key in include) {
|
||||
const obj = {};
|
||||
obj[key] = include[key];
|
||||
newInclude.push(obj);
|
||||
}
|
||||
return newInclude;
|
||||
} else if (Array.isArray(include)) {
|
||||
var normalized = [];
|
||||
for (var i in include) {
|
||||
var includeEntry = include[i];
|
||||
const normalized = [];
|
||||
for (const i in include) {
|
||||
const includeEntry = include[i];
|
||||
if (typeof includeEntry === 'string') {
|
||||
const obj = {};
|
||||
obj[includeEntry] = true;
|
||||
|
@ -184,7 +201,7 @@ function mergeQuery(base, update, spec) {
|
|||
// specify nestedInclude=true to force nesting of inclusions on scoped
|
||||
// queries. e.g. In physician.patients.find({include: 'address'}),
|
||||
// inclusion should be on patient model, not on physician model.
|
||||
var saved = base.include;
|
||||
const saved = base.include;
|
||||
base.include = {};
|
||||
base.include[update.include] = saved;
|
||||
} else {
|
||||
|
@ -216,7 +233,7 @@ function mergeQuery(base, update, spec) {
|
|||
base.limit = update.limit;
|
||||
}
|
||||
|
||||
var skip = spec.skip !== false && spec.offset !== false;
|
||||
const skip = spec.skip !== false && spec.offset !== false;
|
||||
|
||||
if (skip && update.skip !== undefined) {
|
||||
base.skip = update.skip;
|
||||
|
@ -240,8 +257,8 @@ function fieldsToArray(fields, properties, excludeUnknown) {
|
|||
if (!fields) return;
|
||||
|
||||
// include all properties by default
|
||||
var result = properties;
|
||||
var i, n;
|
||||
let result = properties;
|
||||
let i, n;
|
||||
|
||||
if (typeof fields === 'string') {
|
||||
result = [fields];
|
||||
|
@ -250,13 +267,13 @@ function fieldsToArray(fields, properties, excludeUnknown) {
|
|||
result = fields;
|
||||
} else if ('object' === typeof fields) {
|
||||
// { field1: boolean, field2: boolean ... }
|
||||
var included = [];
|
||||
var excluded = [];
|
||||
var keys = Object.keys(fields);
|
||||
const included = [];
|
||||
const excluded = [];
|
||||
const keys = Object.keys(fields);
|
||||
if (!keys.length) return;
|
||||
|
||||
for (i = 0, n = keys.length; i < n; i++) {
|
||||
var k = keys[i];
|
||||
const k = keys[i];
|
||||
if (fields[k]) {
|
||||
included.push(k);
|
||||
} else if ((k in fields) && !fields[k]) {
|
||||
|
@ -267,13 +284,13 @@ function fieldsToArray(fields, properties, excludeUnknown) {
|
|||
result = included;
|
||||
} else if (excluded.length > 0) {
|
||||
for (i = 0, n = excluded.length; i < n; i++) {
|
||||
var index = result.indexOf(excluded[i]);
|
||||
const index = result.indexOf(excluded[i]);
|
||||
if (index !== -1) result.splice(index, 1); // only when existing field excluded
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var fieldArray = [];
|
||||
let fieldArray = [];
|
||||
if (excludeUnknown) {
|
||||
for (i = 0, n = result.length; i < n; i++) {
|
||||
if (properties.indexOf(result[i]) !== -1) {
|
||||
|
@ -289,10 +306,10 @@ function fieldsToArray(fields, properties, excludeUnknown) {
|
|||
function selectFields(fields) {
|
||||
// map function
|
||||
return function(obj) {
|
||||
var result = {};
|
||||
var key;
|
||||
const result = {};
|
||||
let key;
|
||||
|
||||
for (var i = 0; i < fields.length; i++) {
|
||||
for (let i = 0; i < fields.length; i++) {
|
||||
key = fields[i];
|
||||
|
||||
result[key] = obj[key];
|
||||
|
@ -301,27 +318,111 @@ function selectFields(fields) {
|
|||
};
|
||||
}
|
||||
|
||||
function isProhibited(key, prohibitedKeys) {
|
||||
if (!prohibitedKeys || !prohibitedKeys.length) return false;
|
||||
if (typeof key !== 'string') {
|
||||
return false;
|
||||
}
|
||||
for (const k of prohibitedKeys) {
|
||||
if (k === key) return true;
|
||||
// x.secret, secret.y, or x.secret.y
|
||||
if (key.split('.').indexOf(k) !== -1) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove undefined values from the queury object
|
||||
* @param query
|
||||
* @param handleUndefined {String} either "nullify", "throw" or "ignore" (default: "ignore")
|
||||
* @returns {exports.map|*}
|
||||
* Accept an operator key and return whether it is used for a regular expression query or not
|
||||
* @param {string} operator
|
||||
* @returns {boolean}
|
||||
*/
|
||||
function removeUndefined(query, handleUndefined) {
|
||||
function isRegExpOperator(operator) {
|
||||
return ['like', 'nlike', 'ilike', 'nilike', 'regexp'].includes(operator);
|
||||
}
|
||||
|
||||
/**
|
||||
* Accept a RegExp string and make sure that any special characters for RegExp are escaped in case they
|
||||
* create an invalid Regexp
|
||||
* @param {string} str
|
||||
* @returns {string}
|
||||
*/
|
||||
function escapeRegExp(str) {
|
||||
assert.strictEqual(typeof str, 'string', 'String required for regexp escaping');
|
||||
try {
|
||||
new RegExp(str); // try to parse string as regexp
|
||||
return str;
|
||||
} catch (unused) {
|
||||
console.warn(
|
||||
'Auto-escaping invalid RegExp value %j supplied by the caller. ' +
|
||||
'Please note this behavior may change in the future.',
|
||||
str,
|
||||
);
|
||||
return str.replace(/[\-\[\]\/\{\}\(\)\+\?\.\\\^\$\|]/g, '\\$&');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sanitize the query object
|
||||
* @param query {object} The query object
|
||||
* @param options
|
||||
* @property normalizeUndefinedInQuery {String} either "nullify", "throw" or "ignore" (default: "ignore")
|
||||
* @property prohibitedKeys {String[]} An array of prohibited keys to be removed
|
||||
* @returns {*}
|
||||
*/
|
||||
function sanitizeQuery(query, options) {
|
||||
debug('Sanitizing query object: %j', query);
|
||||
if (typeof query !== 'object' || query === null) {
|
||||
return query;
|
||||
}
|
||||
options = options || {};
|
||||
if (typeof options === 'string') {
|
||||
// Keep it backward compatible
|
||||
options = {normalizeUndefinedInQuery: options};
|
||||
}
|
||||
const prohibitedKeys = options.prohibitedKeys;
|
||||
const offendingKeys = [];
|
||||
const normalizeUndefinedInQuery = options.normalizeUndefinedInQuery;
|
||||
const maxDepth = options.maxDepth || Number.MAX_SAFE_INTEGER;
|
||||
// WARNING: [rfeng] Use map() will cause mongodb to produce invalid BSON
|
||||
// as traverse doesn't transform the ObjectId correctly
|
||||
return traverse(query).forEach(function(x) {
|
||||
const result = traverse(query).forEach(function(x) {
|
||||
/**
|
||||
* Security risk if the client passes in a very deep where object
|
||||
*/
|
||||
if (this.circular) {
|
||||
const msg = g.f('The query object is circular');
|
||||
const err = new Error(msg);
|
||||
err.statusCode = 400;
|
||||
err.code = 'QUERY_OBJECT_IS_CIRCULAR';
|
||||
throw err;
|
||||
}
|
||||
if (this.level > maxDepth) {
|
||||
const msg = g.f('The query object exceeds maximum depth %d', maxDepth);
|
||||
const err = new Error(msg);
|
||||
err.statusCode = 400;
|
||||
err.code = 'QUERY_OBJECT_TOO_DEEP';
|
||||
throw err;
|
||||
}
|
||||
/**
|
||||
* Make sure prohibited keys are removed from the query to prevent
|
||||
* sensitive values from being guessed
|
||||
*/
|
||||
if (isProhibited(this.key, prohibitedKeys)) {
|
||||
offendingKeys.push(this.key);
|
||||
this.remove();
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle undefined values
|
||||
*/
|
||||
if (x === undefined) {
|
||||
switch (handleUndefined) {
|
||||
switch (normalizeUndefinedInQuery) {
|
||||
case 'nullify':
|
||||
this.update(null);
|
||||
break;
|
||||
case 'throw':
|
||||
throw new Error(g.f('Unexpected `undefined` in query'));
|
||||
break;
|
||||
case 'ignore':
|
||||
default:
|
||||
this.remove();
|
||||
|
@ -335,12 +436,26 @@ function removeUndefined(query, handleUndefined) {
|
|||
return x;
|
||||
}
|
||||
|
||||
if (isRegExpOperator(this.key) && typeof x === 'string') { // we have regexp supporting operator and string to escape
|
||||
return escapeRegExp(x);
|
||||
}
|
||||
|
||||
return x;
|
||||
});
|
||||
|
||||
if (offendingKeys.length) {
|
||||
console.error(
|
||||
g.f(
|
||||
'Potential security alert: hidden/protected properties %j are used in query.',
|
||||
offendingKeys,
|
||||
),
|
||||
);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
var url = require('url');
|
||||
var qs = require('qs');
|
||||
const url = require('url');
|
||||
const qs = require('qs');
|
||||
|
||||
/**
|
||||
* Parse a URL into a settings object
|
||||
|
@ -351,18 +466,18 @@ function parseSettings(urlStr) {
|
|||
if (!urlStr) {
|
||||
return {};
|
||||
}
|
||||
var uri = url.parse(urlStr, false);
|
||||
var settings = {};
|
||||
const uri = url.parse(urlStr, false);
|
||||
const settings = {};
|
||||
settings.connector = uri.protocol && uri.protocol.split(':')[0]; // Remove the trailing :
|
||||
settings.host = settings.hostname = uri.hostname;
|
||||
settings.port = uri.port && Number(uri.port); // port is a string
|
||||
settings.user = settings.username = uri.auth && uri.auth.split(':')[0]; // <username>:<password>
|
||||
settings.password = uri.auth && uri.auth.split(':')[1];
|
||||
settings.database = uri.pathname && uri.pathname.split('/')[1]; // remove the leading /
|
||||
settings.database = uri.pathname && uri.pathname.split('/')[1]; // remove the leading /
|
||||
settings.url = urlStr;
|
||||
if (uri.query) {
|
||||
var params = qs.parse(uri.query);
|
||||
for (var p in params) {
|
||||
const params = qs.parse(uri.query);
|
||||
for (const p in params) {
|
||||
settings[p] = params[p];
|
||||
}
|
||||
}
|
||||
|
@ -386,8 +501,8 @@ function parseSettings(urlStr) {
|
|||
*/
|
||||
function deepMerge(base, extras) {
|
||||
// deepMerge allows undefined extras to allow deep cloning of arrays
|
||||
var array = Array.isArray(base) && (Array.isArray(extras) || !extras);
|
||||
var dst = array && [] || {};
|
||||
const array = Array.isArray(base) && (Array.isArray(extras) || !extras);
|
||||
let dst = array && [] || {};
|
||||
|
||||
if (array) {
|
||||
// extras or base is an array
|
||||
|
@ -415,7 +530,7 @@ function deepMerge(base, extras) {
|
|||
if (extras != null && typeof extras === 'object') {
|
||||
// extras is an object {}
|
||||
Object.keys(extras).forEach(function(key) {
|
||||
var extra = extras[key];
|
||||
const extra = extras[key];
|
||||
if (extra == null || typeof extra !== 'object') {
|
||||
// extra item value is null, undefined or not an object
|
||||
dst[key] = extra;
|
||||
|
@ -446,8 +561,8 @@ function deepMerge(base, extras) {
|
|||
* @returns {Object} The merged property
|
||||
*/
|
||||
function deepMergeProperty(base, extras) {
|
||||
let mergedObject = deepMerge({key: base}, {key: extras});
|
||||
let mergedProperty = mergedObject.key;
|
||||
const mergedObject = deepMerge({key: base}, {key: extras});
|
||||
const mergedProperty = mergedObject.key;
|
||||
return mergedProperty;
|
||||
}
|
||||
|
||||
|
@ -505,7 +620,7 @@ function defineCachedRelations(obj) {
|
|||
|
||||
/**
|
||||
* Check if the argument is plain object
|
||||
* @param {*) obj The obj value
|
||||
* @param {*} obj The obj value
|
||||
* @returns {boolean}
|
||||
*/
|
||||
function isPlainObject(obj) {
|
||||
|
@ -518,26 +633,26 @@ function sortObjectsByIds(idName, ids, objects, strict) {
|
|||
return (typeof id === 'object') ? String(id) : id;
|
||||
});
|
||||
|
||||
var indexOf = function(x) {
|
||||
var isObj = (typeof x[idName] === 'object'); // ObjectID
|
||||
var id = isObj ? String(x[idName]) : x[idName];
|
||||
const indexOf = function(x) {
|
||||
const isObj = (typeof x[idName] === 'object'); // ObjectID
|
||||
const id = isObj ? String(x[idName]) : x[idName];
|
||||
return ids.indexOf(id);
|
||||
};
|
||||
|
||||
var heading = [];
|
||||
var tailing = [];
|
||||
const heading = [];
|
||||
const tailing = [];
|
||||
|
||||
objects.forEach(function(x) {
|
||||
if (typeof x === 'object') {
|
||||
var idx = indexOf(x);
|
||||
const idx = indexOf(x);
|
||||
if (strict && idx === -1) return;
|
||||
idx === -1 ? tailing.push(x) : heading.push(x);
|
||||
}
|
||||
});
|
||||
|
||||
heading.sort(function(x, y) {
|
||||
var a = indexOf(x);
|
||||
var b = indexOf(y);
|
||||
const a = indexOf(x);
|
||||
const b = indexOf(y);
|
||||
if (a === -1 || b === -1) return 1; // last
|
||||
if (a === b) return 0;
|
||||
if (a > b) return 1;
|
||||
|
@ -545,11 +660,11 @@ function sortObjectsByIds(idName, ids, objects, strict) {
|
|||
});
|
||||
|
||||
return heading.concat(tailing);
|
||||
};
|
||||
}
|
||||
|
||||
function createPromiseCallback() {
|
||||
var cb;
|
||||
var promise = new Promise(function(resolve, reject) {
|
||||
let cb;
|
||||
const promise = new Promise(function(resolve, reject) {
|
||||
cb = function(err, data) {
|
||||
if (err) return reject(err);
|
||||
return resolve(data);
|
||||
|
@ -559,20 +674,27 @@ function createPromiseCallback() {
|
|||
return cb;
|
||||
}
|
||||
|
||||
function isBsonType(value) {
|
||||
// bson@1.x stores _bsontype on ObjectID instance, bson@4.x on prototype
|
||||
return value.hasOwnProperty('_bsontype') ||
|
||||
value.constructor.prototype.hasOwnProperty('_bsontype');
|
||||
}
|
||||
|
||||
/**
|
||||
* Dedupe an array
|
||||
* @param {Array} an array
|
||||
* @returns {Array} an array with unique items
|
||||
*/
|
||||
function uniq(a) {
|
||||
var uniqArray = [];
|
||||
const uniqArray = [];
|
||||
if (!a) {
|
||||
return uniqArray;
|
||||
}
|
||||
assert(Array.isArray(a), 'array argument is required');
|
||||
var comparableA = a.map(
|
||||
item => item.hasOwnProperty('_bsontype') ? item.toString() : item);
|
||||
for (var i = 0, n = comparableA.length; i < n; i++) {
|
||||
const comparableA = a.map(
|
||||
item => isBsonType(item) ? item.toString() : item,
|
||||
);
|
||||
for (let i = 0, n = comparableA.length; i < n; i++) {
|
||||
if (comparableA.indexOf(comparableA[i]) === i) {
|
||||
uniqArray.push(a[i]);
|
||||
}
|
||||
|
@ -586,8 +708,8 @@ function uniq(a) {
|
|||
* @returns {Object} A RegExp object
|
||||
*/
|
||||
function toRegExp(regex) {
|
||||
var isString = typeof regex === 'string';
|
||||
var isRegExp = regex instanceof RegExp;
|
||||
const isString = typeof regex === 'string';
|
||||
const isRegExp = regex instanceof RegExp;
|
||||
|
||||
if (!(isString || isRegExp))
|
||||
return new Error(g.f('Invalid argument, must be a string, {{regex}} literal, or ' +
|
||||
|
@ -600,20 +722,20 @@ function toRegExp(regex) {
|
|||
return new RegExp(regex);
|
||||
|
||||
// only accept i, g, or m as valid regex flags
|
||||
var flags = regex.split('/').pop().split('');
|
||||
var validFlags = ['i', 'g', 'm'];
|
||||
var invalidFlags = [];
|
||||
const flags = regex.split('/').pop().split('');
|
||||
const validFlags = ['i', 'g', 'm'];
|
||||
const invalidFlags = [];
|
||||
flags.forEach(function(flag) {
|
||||
if (validFlags.indexOf(flag) === -1)
|
||||
invalidFlags.push(flag);
|
||||
});
|
||||
|
||||
var hasInvalidFlags = invalidFlags.length > 0;
|
||||
const hasInvalidFlags = invalidFlags.length > 0;
|
||||
if (hasInvalidFlags)
|
||||
return new Error(g.f('Invalid {{regex}} flags: %s', invalidFlags));
|
||||
|
||||
// strip regex delimiter forward slashes
|
||||
var expression = regex.substr(1, regex.lastIndexOf('/') - 1);
|
||||
const expression = regex.substr(1, regex.lastIndexOf('/') - 1);
|
||||
return new RegExp(expression, flags.join(''));
|
||||
}
|
||||
|
||||
|
@ -651,9 +773,9 @@ function findIndexOf(arr, target, isEqual) {
|
|||
return arr.indexOf(target);
|
||||
}
|
||||
|
||||
for (var i = 0; i < arr.length; i++) {
|
||||
for (let i = 0; i < arr.length; i++) {
|
||||
if (isEqual(arr[i], target)) { return i; }
|
||||
};
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
@ -665,12 +787,12 @@ function findIndexOf(arr, target, isEqual) {
|
|||
* @returns {Object} The object that queries targetIds
|
||||
*/
|
||||
function collectTargetIds(targetData, idPropertyName) {
|
||||
var targetIds = [];
|
||||
for (var i = 0; i < targetData.length; i++) {
|
||||
var targetId = targetData[i][idPropertyName];
|
||||
const targetIds = [];
|
||||
for (let i = 0; i < targetData.length; i++) {
|
||||
const targetId = targetData[i][idPropertyName];
|
||||
targetIds.push(targetId);
|
||||
};
|
||||
var IdQuery = {
|
||||
}
|
||||
const IdQuery = {
|
||||
inq: uniq(targetIds),
|
||||
};
|
||||
return IdQuery;
|
||||
|
@ -684,3 +806,90 @@ function collectTargetIds(targetData, idPropertyName) {
|
|||
function idName(m) {
|
||||
return m.definition.idName() || 'id';
|
||||
}
|
||||
|
||||
/**
|
||||
* Check a list of IDs to see if there are any duplicates.
|
||||
*
|
||||
* @param {Array} The array of IDs to check
|
||||
* @returns {boolean} If any duplicates were found
|
||||
*/
|
||||
function idsHaveDuplicates(ids) {
|
||||
// use Set if available and all ids are of string or number type
|
||||
let hasDuplicates = undefined;
|
||||
let i, j;
|
||||
if (typeof Set === 'function') {
|
||||
const uniqueIds = new Set();
|
||||
for (i = 0; i < ids.length; ++i) {
|
||||
const idType = typeof ids[i];
|
||||
if (idType === 'string' || idType === 'number') {
|
||||
if (uniqueIds.has(ids[i])) {
|
||||
hasDuplicates = true;
|
||||
break;
|
||||
} else {
|
||||
uniqueIds.add(ids[i]);
|
||||
}
|
||||
} else {
|
||||
// ids are not all string/number that can be checked via Set, stop and do the slow test
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (hasDuplicates === undefined && uniqueIds.size === ids.length) {
|
||||
hasDuplicates = false;
|
||||
}
|
||||
}
|
||||
if (hasDuplicates === undefined) {
|
||||
// fast check was inconclusive or unavailable, do the slow check
|
||||
// can still optimize this by doing 1/2 N^2 instead of the full N^2
|
||||
for (i = 0; i < ids.length && hasDuplicates === undefined; ++i) {
|
||||
for (j = 0; j < i; ++j) {
|
||||
if (idEquals(ids[i], ids[j])) {
|
||||
hasDuplicates = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return hasDuplicates === true;
|
||||
}
|
||||
|
||||
function isClass(fn) {
|
||||
return fn && fn.toString().startsWith('class ');
|
||||
}
|
||||
|
||||
/**
|
||||
* Accept an element, and attach the __parent property to it, unless no object given, while also
|
||||
* making sure to check for already created properties
|
||||
*
|
||||
* @param {object} element
|
||||
* @param {Model} parent
|
||||
*/
|
||||
function applyParentProperty(element, parent) {
|
||||
assert.strictEqual(typeof element, 'object', 'Non object element given to assign parent');
|
||||
const {constructor: {modelBuilder: {settings: builderSettings} = {}} = {}} = element;
|
||||
if (!builderSettings || !builderSettings[BUILDER_PARENT_SETTING]) {
|
||||
// parentRef flag not enabled on ModelBuilder settings
|
||||
return;
|
||||
}
|
||||
|
||||
if (element.hasOwnProperty(PARENT_PROPERTY_NAME)) {
|
||||
// property already created on model, just assign
|
||||
const existingParent = element[PARENT_PROPERTY_NAME];
|
||||
if (existingParent && existingParent !== parent) {
|
||||
// parent re-assigned (child model assigned to other model instance)
|
||||
g.warn('Re-assigning child model instance to another parent than the original!\n' +
|
||||
'Although supported, this is not a recommended practice: ' +
|
||||
`${element.constructor.name} -> ${parent.constructor.name}\n` +
|
||||
'You should create an independent copy of the child model using `new Model(CHILD)` OR ' +
|
||||
'`new Model(CHILD.toJSON())` and assign to new parent');
|
||||
}
|
||||
element[PARENT_PROPERTY_NAME] = parent;
|
||||
} else {
|
||||
// first time defining the property on the element
|
||||
Object.defineProperty(element, PARENT_PROPERTY_NAME, {
|
||||
value: parent,
|
||||
writable: true,
|
||||
enumerable: false,
|
||||
configurable: false,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,13 +1,13 @@
|
|||
// Copyright IBM Corp. 2013,2016. All Rights Reserved.
|
||||
// Copyright IBM Corp. 2013,2019. All Rights Reserved.
|
||||
// Node module: loopback-datasource-juggler
|
||||
// This file is licensed under the MIT License.
|
||||
// License text available at https://opensource.org/licenses/MIT
|
||||
|
||||
'use strict';
|
||||
|
||||
var g = require('strong-globalize')();
|
||||
var util = require('util');
|
||||
var extend = util._extend;
|
||||
const g = require('strong-globalize')();
|
||||
const util = require('util');
|
||||
const extend = util._extend;
|
||||
|
||||
/*!
|
||||
* Module exports
|
||||
|
@ -307,7 +307,7 @@ function validateAbsence(attr, conf, err, options) {
|
|||
function validateLength(attr, conf, err, options) {
|
||||
if (nullCheck.call(this, attr, conf, err)) return;
|
||||
|
||||
var len = this[attr].length;
|
||||
const len = this[attr].length;
|
||||
if (conf.min && len < conf.min) {
|
||||
err('min');
|
||||
}
|
||||
|
@ -362,7 +362,7 @@ function validateFormat(attr, conf, err, options) {
|
|||
if (nullCheck.call(this, attr, conf, err)) return;
|
||||
|
||||
if (typeof this[attr] === 'string' || typeof this[attr] === 'number') {
|
||||
let regex = new RegExp(conf['with']);
|
||||
const regex = new RegExp(conf['with']);
|
||||
if (!regex.test(this[attr])) {
|
||||
err();
|
||||
}
|
||||
|
@ -397,7 +397,7 @@ function escapeStringRegexp(str) {
|
|||
if (typeof str !== 'string') {
|
||||
throw new TypeError('Expected a string');
|
||||
}
|
||||
var matchOperatorsRe = /[|\\{}()[\]^$+*?.]/g;
|
||||
const matchOperatorsRe = /[|\\{}()[\]^$+*?.]/g;
|
||||
return str.replace(matchOperatorsRe, '\\$&');
|
||||
}
|
||||
|
||||
|
@ -412,7 +412,7 @@ function validateUniqueness(attr, conf, err, options, done) {
|
|||
if (blank(this[attr])) {
|
||||
return process.nextTick(done);
|
||||
}
|
||||
var cond = {where: {}};
|
||||
const cond = {where: {}};
|
||||
|
||||
if (conf && conf.ignoreCase) {
|
||||
cond.where[attr] = new RegExp('^' + escapeStringRegexp(this[attr]) + '$', 'i');
|
||||
|
@ -422,14 +422,14 @@ function validateUniqueness(attr, conf, err, options, done) {
|
|||
|
||||
if (conf && conf.scopedTo) {
|
||||
conf.scopedTo.forEach(function(k) {
|
||||
var val = this[k];
|
||||
const val = this[k];
|
||||
if (val !== undefined)
|
||||
cond.where[k] = this[k];
|
||||
}, this);
|
||||
}
|
||||
|
||||
var idName = this.constructor.definition.idName();
|
||||
var isNewRecord = this.isNewRecord();
|
||||
const idName = this.constructor.definition.idName();
|
||||
const isNewRecord = this.isNewRecord();
|
||||
this.constructor.find(cond, options, function(error, found) {
|
||||
if (error) {
|
||||
err(error);
|
||||
|
@ -452,11 +452,11 @@ function validateUniqueness(attr, conf, err, options, done) {
|
|||
function validateDate(attr, conf, err) {
|
||||
if (this[attr] === null || this[attr] === undefined) return;
|
||||
|
||||
var date = new Date(this[attr]);
|
||||
const date = new Date(this[attr]);
|
||||
if (isNaN(date.getTime())) return err();
|
||||
}
|
||||
|
||||
var validators = {
|
||||
const validators = {
|
||||
presence: validatePresence,
|
||||
absence: validateAbsence,
|
||||
length: validateLength,
|
||||
|
@ -471,7 +471,7 @@ var validators = {
|
|||
|
||||
function getConfigurator(name, opts) {
|
||||
return function() {
|
||||
var args = Array.prototype.slice.call(arguments);
|
||||
const args = Array.prototype.slice.call(arguments);
|
||||
args[1] = args[1] || {};
|
||||
configure(this, name, args, opts);
|
||||
};
|
||||
|
@ -513,10 +513,11 @@ function getConfigurator(name, opts) {
|
|||
*/
|
||||
Validatable.prototype.isValid = function(callback, data, options) {
|
||||
options = options || {};
|
||||
var valid = true, inst = this, wait = 0, async = false;
|
||||
var validations = this.constructor.validations;
|
||||
let valid = true, wait = 0, async = false;
|
||||
const inst = this;
|
||||
const validations = this.constructor.validations;
|
||||
|
||||
var reportDiscardedProperties = this.__strict &&
|
||||
const reportDiscardedProperties = this.__strict &&
|
||||
this.__unknownProperties && this.__unknownProperties.length;
|
||||
|
||||
// exit with success when no errors
|
||||
|
@ -539,13 +540,13 @@ Validatable.prototype.isValid = function(callback, data, options) {
|
|||
});
|
||||
|
||||
this.trigger('validate', function(validationsDone) {
|
||||
var inst = this,
|
||||
asyncFail = false;
|
||||
const inst = this;
|
||||
let asyncFail = false;
|
||||
|
||||
var attrs = Object.keys(validations || {});
|
||||
const attrs = Object.keys(validations || {});
|
||||
|
||||
attrs.forEach(function(attr) {
|
||||
var attrValidations = validations[attr] || [];
|
||||
const attrValidations = validations[attr] || [];
|
||||
attrValidations.forEach(function(v) {
|
||||
if (v.options && v.options.async) {
|
||||
async = true;
|
||||
|
@ -562,10 +563,10 @@ Validatable.prototype.isValid = function(callback, data, options) {
|
|||
});
|
||||
|
||||
if (reportDiscardedProperties) {
|
||||
for (var ix in inst.__unknownProperties) {
|
||||
var key = inst.__unknownProperties[ix];
|
||||
var code = 'unknown-property';
|
||||
var msg = defaultMessages[code];
|
||||
for (const ix in inst.__unknownProperties) {
|
||||
const key = inst.__unknownProperties[ix];
|
||||
const code = 'unknown-property';
|
||||
const msg = defaultMessages[code];
|
||||
inst.errors.add(key, msg, code);
|
||||
valid = false;
|
||||
}
|
||||
|
@ -611,7 +612,7 @@ function cleanErrors(inst) {
|
|||
}
|
||||
|
||||
function validationFailed(inst, attr, conf, options, cb) {
|
||||
var opts = conf.options || {};
|
||||
const opts = conf.options || {};
|
||||
|
||||
if (typeof options === 'function') {
|
||||
cb = options;
|
||||
|
@ -628,13 +629,13 @@ function validationFailed(inst, attr, conf, options, cb) {
|
|||
return false;
|
||||
}
|
||||
|
||||
var fail = false;
|
||||
var validator = validators[conf.validation];
|
||||
var validatorArguments = [];
|
||||
let fail = false;
|
||||
const validator = validators[conf.validation];
|
||||
const validatorArguments = [];
|
||||
validatorArguments.push(attr);
|
||||
validatorArguments.push(conf);
|
||||
validatorArguments.push(function onerror(kind) {
|
||||
var message, code = conf.code || conf.validation;
|
||||
let message, code = conf.code || conf.validation;
|
||||
if (conf.message) {
|
||||
message = conf.message;
|
||||
}
|
||||
|
@ -669,7 +670,7 @@ function validationFailed(inst, attr, conf, options, cb) {
|
|||
}
|
||||
|
||||
function skipValidation(inst, conf, kind) {
|
||||
var doValidate = true;
|
||||
let doValidate = true;
|
||||
if (typeof conf[kind] === 'function') {
|
||||
doValidate = conf[kind].call(inst);
|
||||
if (kind === 'unless') doValidate = !doValidate;
|
||||
|
@ -687,7 +688,7 @@ function skipValidation(inst, conf, kind) {
|
|||
return !doValidate;
|
||||
}
|
||||
|
||||
var defaultMessages = {
|
||||
const defaultMessages = {
|
||||
presence: 'can\'t be blank',
|
||||
absence: 'can\'t be set',
|
||||
'unknown-property': 'is not defined in the model',
|
||||
|
@ -764,7 +765,7 @@ function configure(cls, validation, args, opts) {
|
|||
});
|
||||
}
|
||||
args = [].slice.call(args);
|
||||
var conf;
|
||||
let conf;
|
||||
if (typeof args[args.length - 1] === 'object') {
|
||||
conf = args.pop();
|
||||
} else {
|
||||
|
@ -776,7 +777,7 @@ function configure(cls, validation, args, opts) {
|
|||
conf.validation = validation;
|
||||
args.forEach(function(attr) {
|
||||
if (typeof attr === 'string') {
|
||||
var validation = extend({}, conf);
|
||||
const validation = extend({}, conf);
|
||||
validation.options = opts || {};
|
||||
cls.validations[attr] = cls.validations[attr] || [];
|
||||
cls.validations[attr].push(validation);
|
||||
|
@ -803,7 +804,7 @@ Errors.prototype.add = function(field, message, code) {
|
|||
};
|
||||
|
||||
function ErrorCodes(messages) {
|
||||
var c = this;
|
||||
const c = this;
|
||||
Object.keys(messages).forEach(function(field) {
|
||||
c[field] = messages[field].codes;
|
||||
});
|
||||
|
@ -866,11 +867,11 @@ function ValidationError(obj) {
|
|||
|
||||
this.name = 'ValidationError';
|
||||
|
||||
var context = obj && obj.constructor && obj.constructor.modelName;
|
||||
const context = obj && obj.constructor && obj.constructor.modelName;
|
||||
this.message = g.f(
|
||||
'The %s instance is not valid. Details: %s.',
|
||||
context ? '`' + context + '`' : 'model',
|
||||
formatErrors(obj.errors, obj.toJSON()) || '(unknown)'
|
||||
context ? '`' + context + '`' : 'model',
|
||||
formatErrors(obj.errors, obj.toJSON()) || '(unknown)',
|
||||
);
|
||||
|
||||
this.statusCode = 422;
|
||||
|
@ -894,20 +895,20 @@ function ValidationError(obj) {
|
|||
|
||||
util.inherits(ValidationError, Error);
|
||||
|
||||
var errorHasStackProperty = !!(new Error).stack;
|
||||
const errorHasStackProperty = !!(new Error).stack;
|
||||
|
||||
ValidationError.maxPropertyStringLength = 32;
|
||||
|
||||
function formatErrors(errors, propertyValues) {
|
||||
var DELIM = '; ';
|
||||
const DELIM = '; ';
|
||||
errors = errors || {};
|
||||
return Object.getOwnPropertyNames(errors)
|
||||
.filter(function(propertyName) {
|
||||
return Array.isArray(errors[propertyName]);
|
||||
})
|
||||
.map(function(propertyName) {
|
||||
var messages = errors[propertyName];
|
||||
var propertyValue = propertyValues[propertyName];
|
||||
const messages = errors[propertyName];
|
||||
const propertyValue = propertyValues[propertyName];
|
||||
return messages.map(function(msg) {
|
||||
return formatPropertyError(propertyName, propertyValue, msg);
|
||||
}).join(DELIM);
|
||||
|
@ -916,8 +917,8 @@ function formatErrors(errors, propertyValues) {
|
|||
}
|
||||
|
||||
function formatPropertyError(propertyName, propertyValue, errorMessage) {
|
||||
var formattedValue;
|
||||
var valueType = typeof propertyValue;
|
||||
let formattedValue;
|
||||
const valueType = typeof propertyValue;
|
||||
if (valueType === 'string') {
|
||||
formattedValue = JSON.stringify(truncatePropertyString(propertyValue));
|
||||
} else if (propertyValue instanceof Date) {
|
||||
|
@ -939,13 +940,13 @@ function formatPropertyError(propertyName, propertyValue, errorMessage) {
|
|||
}
|
||||
|
||||
function truncatePropertyString(value) {
|
||||
var len = ValidationError.maxPropertyStringLength;
|
||||
let len = ValidationError.maxPropertyStringLength;
|
||||
if (value.length <= len) return value;
|
||||
|
||||
// preserve few last characters like `}` or `]`, but no more than 3
|
||||
// this way the last `} ]` in the array of objects is included in the message
|
||||
var tail;
|
||||
var m = value.match(/([ \t})\]]+)$/);
|
||||
let tail;
|
||||
const m = value.match(/([ \t})\]]+)$/);
|
||||
if (m) {
|
||||
tail = m[1].slice(-3);
|
||||
len -= tail.length;
|
||||
|
|
File diff suppressed because it is too large
Load Diff
69
package.json
69
package.json
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "loopback-datasource-juggler",
|
||||
"version": "3.19.0",
|
||||
"version": "5.0.9",
|
||||
"publishConfig": {
|
||||
"export-tests": true
|
||||
},
|
||||
|
@ -14,12 +14,13 @@
|
|||
"Juggler",
|
||||
"ORM"
|
||||
],
|
||||
"author": "IBM Corp.",
|
||||
"engines": {
|
||||
"node": ">=4.0.0"
|
||||
"node": ">=18"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/strongloop/loopback-datasource-juggler"
|
||||
"url": "https://github.com/loopbackio/loopback-datasource-juggler"
|
||||
},
|
||||
"main": "index.js",
|
||||
"types": "index.d.ts",
|
||||
|
@ -27,46 +28,42 @@
|
|||
"depd": "./lib/browser.depd.js"
|
||||
},
|
||||
"scripts": {
|
||||
"coverage": "nyc report --reporter=text-lcov | coveralls",
|
||||
"lint": "eslint .",
|
||||
"tsc": "tsc -p tsconfig.json --outDir dist",
|
||||
"build": "npm run build-ts-types",
|
||||
"build-ts-types": "tsc -p tsconfig.json --outDir dist",
|
||||
"pretest": "npm run build",
|
||||
"test": "nyc mocha",
|
||||
"posttest": "npm run tsc && npm run lint"
|
||||
"posttest": "npm run lint"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@commitlint/cli": "^19.3.0",
|
||||
"@commitlint/config-conventional": "^19.2.2",
|
||||
"@types/node": "^10.17.60",
|
||||
"async-iterators": "^0.2.2",
|
||||
"bson": "^1.0.4",
|
||||
"coveralls": "^2.13.1",
|
||||
"eslint": "^3.12.2",
|
||||
"eslint-config-loopback": "^8.0.0",
|
||||
"bson": "^4.7.2",
|
||||
"eslint": "^8.57.0",
|
||||
"eslint-config-loopback": "^13.1.0",
|
||||
"eslint-plugin-mocha": "^10.4.3",
|
||||
"loopback-connector-throwing": "file:./test/fixtures/loopback-connector-throwing",
|
||||
"mocha": "^3.2.0",
|
||||
"nyc": "^11.1.0",
|
||||
"should": "^8.4.0",
|
||||
"typescript": "^2.8.3"
|
||||
"mocha": "^10.4.0",
|
||||
"nyc": "^15.1.0",
|
||||
"should": "^13.2.3",
|
||||
"typescript": "^5.4.5"
|
||||
},
|
||||
"dependencies": {
|
||||
"@types/node": "^10.0.3",
|
||||
"async": "~2.1.4",
|
||||
"bluebird": "^3.1.1",
|
||||
"debug": "^3.1.0",
|
||||
"depd": "^1.0.0",
|
||||
"inflection": "^1.6.0",
|
||||
"lodash": "^4.17.4",
|
||||
"loopback-connector": "^4.4.0",
|
||||
"minimatch": "^3.0.3",
|
||||
"qs": "^6.5.0",
|
||||
"shortid": "^2.2.6",
|
||||
"strong-globalize": "^3.1.0",
|
||||
"traverse": "^0.6.6",
|
||||
"uuid": "^3.0.1"
|
||||
"async": "^3.2.5",
|
||||
"change-case": "^4.1.2",
|
||||
"debug": "^4.3.4",
|
||||
"depd": "^2.0.0",
|
||||
"inflection": "^3.0.0",
|
||||
"lodash": "^4.17.21",
|
||||
"loopback-connector": "^6.1.5",
|
||||
"minimatch": "^9.0.4",
|
||||
"nanoid": "^3.3.7",
|
||||
"qs": "^6.12.1",
|
||||
"strong-globalize": "^6.0.6",
|
||||
"traverse": "^0.6.9",
|
||||
"uuid": "^9.0.1"
|
||||
},
|
||||
"license": "MIT",
|
||||
"ci": {
|
||||
"downstreamIgnoreList": [
|
||||
"loopback-connector-db2z",
|
||||
"loopback-connector-informix",
|
||||
"loopback-connector-mqlight"
|
||||
]
|
||||
}
|
||||
"license": "MIT"
|
||||
}
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
{
|
||||
"extends": [
|
||||
"github>loopbackio/cicd//shared-configs/renovate/base"
|
||||
]
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
// Copyright IBM Corp. 2015,2016. All Rights Reserved.
|
||||
// Copyright IBM Corp. 2015,2019. All Rights Reserved.
|
||||
// Node module: loopback-datasource-juggler
|
||||
// This file is licensed under the MIT License.
|
||||
// License text available at https://opensource.org/licenses/MIT
|
||||
|
@ -12,30 +12,30 @@
|
|||
* $ open hooks.hml
|
||||
*
|
||||
*/
|
||||
var Promise = global.Promise = require('bluebird');
|
||||
var DataSource = require('../').DataSource;
|
||||
var Memory = require('../lib/connectors/memory').Memory;
|
||||
const Promise = global.Promise = require('bluebird');
|
||||
const DataSource = require('../').DataSource;
|
||||
const Memory = require('../lib/connectors/memory').Memory;
|
||||
|
||||
var HOOK_NAMES = [
|
||||
const HOOK_NAMES = [
|
||||
'access',
|
||||
'before save', 'persist', 'loaded', 'after save',
|
||||
'before delete', 'after delete',
|
||||
];
|
||||
|
||||
var dataSources = [
|
||||
const dataSources = [
|
||||
createOptimizedDataSource(),
|
||||
createUnoptimizedDataSource(),
|
||||
];
|
||||
|
||||
var observedContexts = [];
|
||||
var lastId = 0;
|
||||
const observedContexts = [];
|
||||
let lastId = 0;
|
||||
|
||||
Promise.onPossiblyUnhandledRejection(function(err) {
|
||||
console.error('POSSIBLY UNHANDLED REJECTION', err.stack);
|
||||
});
|
||||
|
||||
/* eslint-disable camelcase */
|
||||
var operations = [
|
||||
const operations = [
|
||||
function find(ds) {
|
||||
return ds.TestModel.find({where: {id: '1'}});
|
||||
},
|
||||
|
@ -51,13 +51,15 @@ var operations = [
|
|||
function findOrCreate_found(ds) {
|
||||
return ds.TestModel.findOrCreate(
|
||||
{where: {name: ds.existingInstance.name}},
|
||||
{name: ds.existingInstance.name});
|
||||
{name: ds.existingInstance.name},
|
||||
);
|
||||
},
|
||||
|
||||
function findOrCreate_create(ds) {
|
||||
return ds.TestModel.findOrCreate(
|
||||
{where: {name: 'new-record'}},
|
||||
{name: 'new-record'});
|
||||
{name: 'new-record'},
|
||||
);
|
||||
},
|
||||
|
||||
function updateOrCreate_create(ds) {
|
||||
|
@ -66,7 +68,8 @@ var operations = [
|
|||
|
||||
function updateOrCreate_update(ds) {
|
||||
return ds.TestModel.updateOrCreate(
|
||||
{id: ds.existingInstance.id, name: 'new name'});
|
||||
{id: ds.existingInstance.id, name: 'new name'},
|
||||
);
|
||||
},
|
||||
|
||||
function replaceOrCreate_create(ds) {
|
||||
|
@ -75,13 +78,15 @@ var operations = [
|
|||
|
||||
function replaceOrCreate_update(ds) {
|
||||
return ds.TestModel.replaceOrCreate(
|
||||
{id: ds.existingInstance.id, name: 'new name'});
|
||||
{id: ds.existingInstance.id, name: 'new name'},
|
||||
);
|
||||
},
|
||||
|
||||
function replaceById(ds) {
|
||||
return ds.TestModel.replaceById(
|
||||
ds.existingInstance.id,
|
||||
{name: 'new name'});
|
||||
{name: 'new name'},
|
||||
);
|
||||
},
|
||||
|
||||
function updateAll(ds) {
|
||||
|
@ -107,7 +112,7 @@ var operations = [
|
|||
];
|
||||
/* eslint-enable camelcase */
|
||||
|
||||
var p = setupTestModels();
|
||||
let p = setupTestModels();
|
||||
operations.forEach(function(op) {
|
||||
p = p.then(runner(op));
|
||||
});
|
||||
|
@ -115,13 +120,13 @@ operations.forEach(function(op) {
|
|||
p.then(report, function(err) { console.error(err.stack); });
|
||||
|
||||
function createOptimizedDataSource() {
|
||||
var ds = new DataSource({connector: Memory});
|
||||
const ds = new DataSource({connector: Memory});
|
||||
ds.name = 'Optimized';
|
||||
return ds;
|
||||
}
|
||||
|
||||
function createUnoptimizedDataSource() {
|
||||
var ds = new DataSource({connector: Memory});
|
||||
const ds = new DataSource({connector: Memory});
|
||||
ds.name = 'Unoptimized';
|
||||
|
||||
// disable optimized methods
|
||||
|
@ -134,7 +139,7 @@ function createUnoptimizedDataSource() {
|
|||
|
||||
function setupTestModels() {
|
||||
dataSources.forEach(function setupOnDataSource(ds) {
|
||||
var TestModel = ds.TestModel = ds.createModel('TestModel', {
|
||||
const TestModel = ds.TestModel = ds.createModel('TestModel', {
|
||||
id: {type: String, id: true, default: uid},
|
||||
name: {type: String, required: true},
|
||||
extra: {type: String, required: false},
|
||||
|
@ -150,7 +155,7 @@ function uid() {
|
|||
|
||||
function runner(fn) {
|
||||
return function() {
|
||||
var res = Promise.resolve();
|
||||
let res = Promise.resolve();
|
||||
dataSources.forEach(function(ds) {
|
||||
res = res.then(function() {
|
||||
return resetStorage(ds);
|
||||
|
@ -168,7 +173,7 @@ function runner(fn) {
|
|||
}
|
||||
|
||||
function resetStorage(ds) {
|
||||
var TestModel = ds.TestModel;
|
||||
const TestModel = ds.TestModel;
|
||||
HOOK_NAMES.forEach(function(hook) {
|
||||
TestModel.clearObservers(hook);
|
||||
});
|
||||
|
@ -187,7 +192,7 @@ function resetStorage(ds) {
|
|||
.then(function() {
|
||||
HOOK_NAMES.forEach(function(hook) {
|
||||
TestModel.observe(hook, function(ctx, next) {
|
||||
var row = observedContexts[observedContexts.length - 1];
|
||||
const row = observedContexts[observedContexts.length - 1];
|
||||
row.hooks[hook] = Object.keys(ctx);
|
||||
next();
|
||||
});
|
||||
|
@ -207,7 +212,7 @@ function report() {
|
|||
// merge rows where Optimized and Unoptimized produce the same context
|
||||
observedContexts.forEach(function(row, ix) {
|
||||
if (!ix) return;
|
||||
var last = observedContexts[ix - 1];
|
||||
const last = observedContexts[ix - 1];
|
||||
if (row.operation != last.operation) return;
|
||||
if (JSON.stringify(row.hooks) !== JSON.stringify(last.hooks)) return;
|
||||
last.merge = true;
|
||||
|
@ -221,11 +226,11 @@ function report() {
|
|||
|
||||
observedContexts.forEach(function(row) {
|
||||
if (row.skip) return;
|
||||
var caption = row.operation;
|
||||
let caption = row.operation;
|
||||
if (!row.merge) caption += ' (' + row.connector + ')';
|
||||
console.log('<tr><th>' + caption + '</th>');
|
||||
HOOK_NAMES.forEach(function(h) {
|
||||
var text = row.hooks[h] ? row.hooks[h].join('<br/>') : '';
|
||||
const text = row.hooks[h] ? row.hooks[h].join('<br/>') : '';
|
||||
console.log(' <td>' + text + '</td>');
|
||||
});
|
||||
console.log('</tr>');
|
||||
|
|
|
@ -1,15 +1,16 @@
|
|||
// Copyright IBM Corp. 2015,2016. All Rights Reserved.
|
||||
// Copyright IBM Corp. 2015,2019. All Rights Reserved.
|
||||
// Node module: loopback-datasource-juggler
|
||||
// This file is licensed under the MIT License.
|
||||
// License text available at https://opensource.org/licenses/MIT
|
||||
|
||||
'use strict';
|
||||
|
||||
var should = require('./init.js');
|
||||
const should = require('./init.js');
|
||||
|
||||
var jdb = require('../');
|
||||
var DataSource = jdb.DataSource;
|
||||
const jdb = require('../');
|
||||
const DataSource = jdb.DataSource;
|
||||
|
||||
var ds, Item, Variant;
|
||||
let ds, Item, Variant;
|
||||
describe('Datasource-specific field types for foreign keys', function() {
|
||||
before(function() {
|
||||
ds = new DataSource('memory');
|
||||
|
@ -35,7 +36,7 @@ describe('Datasource-specific field types for foreign keys', function() {
|
|||
});
|
||||
|
||||
it('should create foreign key with database-specific field type', function(done) {
|
||||
var VariantDefinition = ds.getModelDefinition('Variant');
|
||||
const VariantDefinition = ds.getModelDefinition('Variant');
|
||||
should.exist(VariantDefinition);
|
||||
should.exist(VariantDefinition.properties.myProp.memory);
|
||||
should.exist(VariantDefinition.properties.myProp.memory.dataType);
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// Copyright IBM Corp. 2015,2016. All Rights Reserved.
|
||||
// Copyright IBM Corp. 2016,2019. All Rights Reserved.
|
||||
// Node module: loopback-datasource-juggler
|
||||
// This file is licensed under the MIT License.
|
||||
// License text available at https://opensource.org/licenses/MIT
|
||||
|
@ -60,57 +60,66 @@ describe('allowExtendedOperators', () => {
|
|||
|
||||
all(model, filter, options, callback) {
|
||||
// return the raw "value" query
|
||||
let instanceFound = {
|
||||
const instanceFound = {
|
||||
value: filter.where.value,
|
||||
};
|
||||
callback(null, [instanceFound]);
|
||||
}
|
||||
}
|
||||
|
||||
function assertOperatorNotAllowed(err) {
|
||||
should.exist(err);
|
||||
err.message.should.match(/Operators "\$exists" are not allowed in query/);
|
||||
err.code.should.equal('OPERATOR_NOT_ALLOWED_IN_QUERY');
|
||||
err.statusCode.should.equal(400);
|
||||
err.details.should.have.property('operators');
|
||||
err.details.should.have.property('where');
|
||||
}
|
||||
|
||||
describe('dataSource.settings.allowExtendedOperators', () => {
|
||||
context('DAO.find()', () => {
|
||||
it('converts extended operators to string value by default', () => {
|
||||
it('reports invalid operator by default', () => {
|
||||
const TestModel = createTestModel();
|
||||
return TestModel.find(extendedQuery()).then((results) => {
|
||||
should(results[0].value).eql('[object Object]');
|
||||
return TestModel.find(extendedQuery()).catch(err => {
|
||||
assertOperatorNotAllowed(err);
|
||||
});
|
||||
});
|
||||
|
||||
it('preserves extended operators with allowExtendedOperators set', () => {
|
||||
const TestModel = createTestModel({allowExtendedOperators: true});
|
||||
return TestModel.find(extendedQuery()).then((results) => {
|
||||
return TestModel.find(extendedQuery()).then(results => {
|
||||
should(results[0].value).eql({$exists: true});
|
||||
});
|
||||
});
|
||||
|
||||
it('`Model.settings.allowExtendedOperators` override data source settings - ' +
|
||||
'converts extended operators', () => {
|
||||
'reports invalid operator', () => {
|
||||
const TestModel = createTestModel({allowExtendedOperators: true}, {allowExtendedOperators: false});
|
||||
return TestModel.find(extendedQuery()).then((results) => {
|
||||
should(results[0].value).eql('[object Object]');
|
||||
return TestModel.find(extendedQuery()).catch(err => {
|
||||
assertOperatorNotAllowed(err);
|
||||
});
|
||||
});
|
||||
|
||||
it('`Model.settings.allowExtendedOperators` override data source settings - ' +
|
||||
'preserves extended operators', () => {
|
||||
const TestModel = createTestModel({allowExtendedOperators: false}, {allowExtendedOperators: true});
|
||||
return TestModel.find(extendedQuery()).then((results) => {
|
||||
return TestModel.find(extendedQuery()).then(results => {
|
||||
should(results[0].value).eql({$exists: true});
|
||||
});
|
||||
});
|
||||
|
||||
it('`options.allowExtendedOperators` override data source settings - ' +
|
||||
'converts extended operators', () => {
|
||||
'reports invalid operator', () => {
|
||||
const TestModel = createTestModel({allowExtendedOperators: true});
|
||||
return TestModel.find(extendedQuery(), {allowExtendedOperators: false}).then((results) => {
|
||||
should(results[0].value).eql('[object Object]');
|
||||
return TestModel.find(extendedQuery(), {allowExtendedOperators: false}).catch(err => {
|
||||
assertOperatorNotAllowed(err);
|
||||
});
|
||||
});
|
||||
|
||||
it('`options.allowExtendedOperators` override data source settings - ' +
|
||||
'preserves extended operators', () => {
|
||||
const TestModel = createTestModel({allowExtendedOperators: false});
|
||||
return TestModel.find(extendedQuery(), {allowExtendedOperators: true}).then((results) => {
|
||||
return TestModel.find(extendedQuery(), {allowExtendedOperators: true}).then(results => {
|
||||
should(results[0].value).eql({$exists: true});
|
||||
});
|
||||
});
|
||||
|
@ -168,37 +177,37 @@ describe('allowExtendedOperators', () => {
|
|||
context('DAO.find()', () => {
|
||||
it('preserves extended operators with allowExtendedOperators set', () => {
|
||||
const TestModel = createTestModel({}, {allowExtendedOperators: true});
|
||||
return TestModel.find(extendedQuery()).then((results) => {
|
||||
return TestModel.find(extendedQuery()).then(results => {
|
||||
should(results[0].value).eql({$exists: true});
|
||||
});
|
||||
});
|
||||
|
||||
it('`dataSource.settings.allowExtendedOperators` honor Model settings - ' +
|
||||
'converts extended operators', () => {
|
||||
'reports invalid operator', () => {
|
||||
const TestModel = createTestModel({allowExtendedOperators: true}, {allowExtendedOperators: false});
|
||||
return TestModel.find(extendedQuery()).then((results) => {
|
||||
should(results[0].value).eql('[object Object]');
|
||||
return TestModel.find(extendedQuery()).catch(err => {
|
||||
assertOperatorNotAllowed(err);
|
||||
});
|
||||
});
|
||||
|
||||
it('`dataSource.settings.allowExtendedOperators` honor Model settings - ' +
|
||||
'preserves extended operators', () => {
|
||||
const TestModel = createTestModel({allowExtendedOperators: false}, {allowExtendedOperators: true});
|
||||
return TestModel.find(extendedQuery()).then((results) => {
|
||||
return TestModel.find(extendedQuery()).then(results => {
|
||||
should(results[0].value).eql({$exists: true});
|
||||
});
|
||||
});
|
||||
|
||||
it('`options.allowExtendedOperators` override Model settings - converts extended operators', () => {
|
||||
const TestModel = createTestModel({allowExtendedOperators: true});
|
||||
return TestModel.find(extendedQuery(), {allowExtendedOperators: false}).then((results) => {
|
||||
should(results[0].value).eql('[object Object]');
|
||||
return TestModel.find(extendedQuery(), {allowExtendedOperators: false}).catch(err => {
|
||||
assertOperatorNotAllowed(err);
|
||||
});
|
||||
});
|
||||
|
||||
it('`options.allowExtendedOperators` Model settings - preserves extended operators', () => {
|
||||
const TestModel = createTestModel({allowExtendedOperators: false});
|
||||
return TestModel.find(extendedQuery(), {allowExtendedOperators: true}).then((results) => {
|
||||
return TestModel.find(extendedQuery(), {allowExtendedOperators: true}).then(results => {
|
||||
should(results[0].value).eql({$exists: true});
|
||||
});
|
||||
});
|
||||
|
@ -255,7 +264,7 @@ describe('allowExtendedOperators', () => {
|
|||
context('DAO.find()', () => {
|
||||
it('preserves extended operators with allowExtendedOperators set', () => {
|
||||
const TestModel = createTestModel();
|
||||
return TestModel.find(extendedQuery(), {allowExtendedOperators: true}).then((results) => {
|
||||
return TestModel.find(extendedQuery(), {allowExtendedOperators: true}).then(results => {
|
||||
should(results[0].value).eql({$exists: true});
|
||||
});
|
||||
});
|
||||
|
@ -263,15 +272,15 @@ describe('allowExtendedOperators', () => {
|
|||
it('`dataSource.settings.allowExtendedOperators` honor options settings - ' +
|
||||
'converts extended operators', () => {
|
||||
const TestModel = createTestModel({allowExtendedOperators: true});
|
||||
return TestModel.find(extendedQuery(), {allowExtendedOperators: false}).then((results) => {
|
||||
should(results[0].value).eql('[object Object]');
|
||||
return TestModel.find(extendedQuery(), {allowExtendedOperators: false}).catch(err => {
|
||||
assertOperatorNotAllowed(err);
|
||||
});
|
||||
});
|
||||
|
||||
it('`dataSource.settings.allowExtendedOperators` honor options settings - ' +
|
||||
'preserves extended operators', () => {
|
||||
const TestModel = createTestModel({allowExtendedOperators: false});
|
||||
return TestModel.find(extendedQuery(), {allowExtendedOperators: true}).then((results) => {
|
||||
return TestModel.find(extendedQuery(), {allowExtendedOperators: true}).then(results => {
|
||||
should(results[0].value).eql({$exists: true});
|
||||
});
|
||||
});
|
||||
|
@ -279,15 +288,15 @@ describe('allowExtendedOperators', () => {
|
|||
it('`Model.settings.allowExtendedOperators` honor options settings - ' +
|
||||
'converts extended operators', () => {
|
||||
const TestModel = createTestModel({}, {allowExtendedOperators: true});
|
||||
return TestModel.find(extendedQuery(), {allowExtendedOperators: false}).then((results) => {
|
||||
should(results[0].value).eql('[object Object]');
|
||||
return TestModel.find(extendedQuery(), {allowExtendedOperators: false}).catch(err => {
|
||||
assertOperatorNotAllowed(err);
|
||||
});
|
||||
});
|
||||
|
||||
it('`Model.settings.allowExtendedOperators` honor options settings - ' +
|
||||
'preserves extended operators', () => {
|
||||
const TestModel = createTestModel({}, {allowExtendedOperators: false});
|
||||
return TestModel.find(extendedQuery(), {allowExtendedOperators: true}).then((results) => {
|
||||
return TestModel.find(extendedQuery(), {allowExtendedOperators: true}).then(results => {
|
||||
should(results[0].value).eql({$exists: true});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -1,22 +1,22 @@
|
|||
// Copyright IBM Corp. 2015,2016. All Rights Reserved.
|
||||
// Copyright IBM Corp. 2015,2019. All Rights Reserved.
|
||||
// Node module: loopback-datasource-juggler
|
||||
// This file is licensed under the MIT License.
|
||||
// License text available at https://opensource.org/licenses/MIT
|
||||
|
||||
'use strict';
|
||||
|
||||
var ModelBuilder = require('../').ModelBuilder;
|
||||
var should = require('./init');
|
||||
var Promise = require('bluebird');
|
||||
const ModelBuilder = require('../').ModelBuilder;
|
||||
const should = require('./init');
|
||||
|
||||
describe('async observer', function() {
|
||||
var TestModel;
|
||||
let TestModel;
|
||||
beforeEach(function defineTestModel() {
|
||||
var modelBuilder = new ModelBuilder();
|
||||
const modelBuilder = new ModelBuilder();
|
||||
TestModel = modelBuilder.define('TestModel', {name: String});
|
||||
});
|
||||
|
||||
it('calls registered async observers', function(done) {
|
||||
var notifications = [];
|
||||
const notifications = [];
|
||||
TestModel.observe('before', pushAndNext(notifications, 'before'));
|
||||
TestModel.observe('after', pushAndNext(notifications, 'after'));
|
||||
|
||||
|
@ -33,7 +33,7 @@ describe('async observer', function() {
|
|||
});
|
||||
|
||||
it('allows multiple observers for the same operation', function(done) {
|
||||
var notifications = [];
|
||||
const notifications = [];
|
||||
TestModel.observe('event', pushAndNext(notifications, 'one'));
|
||||
TestModel.observe('event', pushAndNext(notifications, 'two'));
|
||||
|
||||
|
@ -45,7 +45,7 @@ describe('async observer', function() {
|
|||
});
|
||||
|
||||
it('allows multiple operations to be notified in one call', function(done) {
|
||||
var notifications = [];
|
||||
const notifications = [];
|
||||
TestModel.observe('event1', pushAndNext(notifications, 'one'));
|
||||
TestModel.observe('event2', pushAndNext(notifications, 'two'));
|
||||
|
||||
|
@ -57,10 +57,10 @@ describe('async observer', function() {
|
|||
});
|
||||
|
||||
it('inherits observers from base model', function(done) {
|
||||
var notifications = [];
|
||||
const notifications = [];
|
||||
TestModel.observe('event', pushAndNext(notifications, 'base'));
|
||||
|
||||
var Child = TestModel.extend('Child');
|
||||
const Child = TestModel.extend('Child');
|
||||
Child.observe('event', pushAndNext(notifications, 'child'));
|
||||
|
||||
Child.notifyObserversOf('event', {}, function(err) {
|
||||
|
@ -71,11 +71,11 @@ describe('async observer', function() {
|
|||
});
|
||||
|
||||
it('allow multiple operations to be notified with base models', function(done) {
|
||||
var notifications = [];
|
||||
const notifications = [];
|
||||
TestModel.observe('event1', pushAndNext(notifications, 'base1'));
|
||||
TestModel.observe('event2', pushAndNext(notifications, 'base2'));
|
||||
|
||||
var Child = TestModel.extend('Child');
|
||||
const Child = TestModel.extend('Child');
|
||||
Child.observe('event1', pushAndNext(notifications, 'child1'));
|
||||
Child.observe('event2', pushAndNext(notifications, 'child2'));
|
||||
|
||||
|
@ -87,10 +87,10 @@ describe('async observer', function() {
|
|||
});
|
||||
|
||||
it('does not modify observers in the base model', function(done) {
|
||||
var notifications = [];
|
||||
const notifications = [];
|
||||
TestModel.observe('event', pushAndNext(notifications, 'base'));
|
||||
|
||||
var Child = TestModel.extend('Child');
|
||||
const Child = TestModel.extend('Child');
|
||||
Child.observe('event', pushAndNext(notifications, 'child'));
|
||||
|
||||
TestModel.notifyObserversOf('event', {}, function(err) {
|
||||
|
@ -101,10 +101,10 @@ describe('async observer', function() {
|
|||
});
|
||||
|
||||
it('always calls inherited observers', function(done) {
|
||||
var notifications = [];
|
||||
const notifications = [];
|
||||
TestModel.observe('event', pushAndNext(notifications, 'base'));
|
||||
|
||||
var Child = TestModel.extend('Child');
|
||||
const Child = TestModel.extend('Child');
|
||||
// Important: there are no observers on the Child model
|
||||
|
||||
Child.notifyObserversOf('event', {}, function(err) {
|
||||
|
@ -115,12 +115,12 @@ describe('async observer', function() {
|
|||
});
|
||||
|
||||
it('can remove observers', function(done) {
|
||||
var notifications = [];
|
||||
const notifications = [];
|
||||
|
||||
function call(ctx, next) {
|
||||
notifications.push('call');
|
||||
process.nextTick(next);
|
||||
};
|
||||
}
|
||||
|
||||
TestModel.observe('event', call);
|
||||
TestModel.removeObserver('event', call);
|
||||
|
@ -133,12 +133,12 @@ describe('async observer', function() {
|
|||
});
|
||||
|
||||
it('can clear all observers', function(done) {
|
||||
var notifications = [];
|
||||
const notifications = [];
|
||||
|
||||
function call(ctx, next) {
|
||||
notifications.push('call');
|
||||
process.nextTick(next);
|
||||
};
|
||||
}
|
||||
|
||||
TestModel.observe('event', call);
|
||||
TestModel.observe('event', call);
|
||||
|
@ -160,7 +160,7 @@ describe('async observer', function() {
|
|||
});
|
||||
|
||||
it('passes context to final callback', function(done) {
|
||||
var context = {};
|
||||
const context = {};
|
||||
TestModel.notifyObserversOf('event', context, function(err, ctx) {
|
||||
(ctx || 'null').should.equal(context);
|
||||
done();
|
||||
|
@ -168,7 +168,7 @@ describe('async observer', function() {
|
|||
});
|
||||
|
||||
describe('notifyObserversAround', function() {
|
||||
var notifications;
|
||||
let notifications;
|
||||
beforeEach(function() {
|
||||
notifications = [];
|
||||
TestModel.observe('before execute',
|
||||
|
@ -178,7 +178,7 @@ describe('async observer', function() {
|
|||
});
|
||||
|
||||
it('should notify before/after observers', function(done) {
|
||||
var context = {};
|
||||
const context = {};
|
||||
|
||||
function work(done) {
|
||||
process.nextTick(function() {
|
||||
|
@ -195,7 +195,7 @@ describe('async observer', function() {
|
|||
});
|
||||
|
||||
it('should allow work with context', function(done) {
|
||||
var context = {};
|
||||
const context = {};
|
||||
|
||||
function work(context, done) {
|
||||
process.nextTick(function() {
|
||||
|
@ -213,7 +213,7 @@ describe('async observer', function() {
|
|||
|
||||
it('should notify before/after observers with multiple results',
|
||||
function(done) {
|
||||
var context = {};
|
||||
const context = {};
|
||||
|
||||
function work(done) {
|
||||
process.nextTick(function() {
|
||||
|
@ -240,7 +240,7 @@ describe('async observer', function() {
|
|||
TestModel.observe('after invoke',
|
||||
pushAndNext(notifications, 'after invoke'));
|
||||
|
||||
var context = {};
|
||||
const context = {};
|
||||
|
||||
function work(done) {
|
||||
process.nextTick(function() {
|
||||
|
@ -265,7 +265,7 @@ describe('async observer', function() {
|
|||
next();
|
||||
});
|
||||
|
||||
var context = {};
|
||||
const context = {};
|
||||
|
||||
function work(done) {
|
||||
process.nextTick(function() {
|
||||
|
@ -293,7 +293,7 @@ describe('async observer', function() {
|
|||
});
|
||||
|
||||
it('handles rejected promise returned by an observer', function(done) {
|
||||
var testError = new Error('expected test error');
|
||||
const testError = new Error('expected test error');
|
||||
TestModel.observe('event', function(ctx) {
|
||||
return Promise.reject(testError);
|
||||
});
|
||||
|
@ -304,8 +304,8 @@ describe('async observer', function() {
|
|||
});
|
||||
|
||||
it('returns a promise when no callback is provided', function() {
|
||||
var context = {value: 'a-test-context'};
|
||||
var p = TestModel.notifyObserversOf('event', context);
|
||||
const context = {value: 'a-test-context'};
|
||||
const p = TestModel.notifyObserversOf('event', context);
|
||||
(p !== undefined).should.be.true;
|
||||
return p.then(function(result) {
|
||||
result.should.eql(context);
|
||||
|
@ -313,16 +313,97 @@ describe('async observer', function() {
|
|||
});
|
||||
|
||||
it('returns a rejected promise when no callback is provided', function() {
|
||||
var testError = new Error('expected test error');
|
||||
const testError = new Error('expected test error');
|
||||
TestModel.observe('event', function(ctx, next) { next(testError); });
|
||||
var p = TestModel.notifyObserversOf('event', context);
|
||||
const p = TestModel.notifyObserversOf('event', context);
|
||||
return p.then(
|
||||
function(result) {
|
||||
throw new Error('The promise should have been rejected.');
|
||||
},
|
||||
function(err) {
|
||||
err.should.eql(testError);
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
it('should call after operation hook on error', function(done) {
|
||||
const context = {
|
||||
req: {},
|
||||
};
|
||||
const operationError = new Error('The operation failed without result');
|
||||
let callCount = 0;
|
||||
|
||||
function fail(context, done) {
|
||||
process.nextTick(() => {
|
||||
done(operationError);
|
||||
});
|
||||
}
|
||||
|
||||
TestModel.observe('after execute error', function(ctx, next) {
|
||||
callCount++;
|
||||
next();
|
||||
});
|
||||
|
||||
TestModel.notifyObserversAround('execute', context, fail, (err, ctx) => {
|
||||
callCount.should.eql(1);
|
||||
err.message.should.eql(operationError.message);
|
||||
ctx.error.message.should.eql(operationError.message);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should call after operation hook on error while overwriting error', function(done) {
|
||||
const context = {
|
||||
req: {},
|
||||
};
|
||||
const operationError = new Error('The operation failed without result');
|
||||
const overwriteError = new Error('Overwriting the original error');
|
||||
let callCount = 0;
|
||||
|
||||
function fail(context, done) {
|
||||
process.nextTick(() => {
|
||||
done(operationError);
|
||||
});
|
||||
}
|
||||
|
||||
TestModel.observe('after execute error', function(ctx, next) {
|
||||
callCount++;
|
||||
next(overwriteError);
|
||||
});
|
||||
|
||||
TestModel.notifyObserversAround('execute', context, fail, (err, ctx) => {
|
||||
callCount.should.eql(1);
|
||||
err.message.should.eql(overwriteError.message);
|
||||
ctx.error.message.should.eql(operationError.message);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should call after operation hook on error while allowing to change err', function(done) {
|
||||
const context = {
|
||||
req: {},
|
||||
};
|
||||
const operationError = new Error('The operation failed without result');
|
||||
let callCount = 0;
|
||||
|
||||
function fail(context, done) {
|
||||
process.nextTick(() => {
|
||||
done(operationError);
|
||||
});
|
||||
}
|
||||
|
||||
TestModel.observe('after execute error', function(ctx, next) {
|
||||
callCount++;
|
||||
const err = ctx.error;
|
||||
next(err, ctx);
|
||||
});
|
||||
|
||||
TestModel.notifyObserversAround('execute', context, fail, (err, ctx) => {
|
||||
callCount.should.eql(1);
|
||||
err.message.should.eql(operationError.message);
|
||||
ctx.error.message.should.eql(operationError.message);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -1,17 +1,17 @@
|
|||
// Copyright IBM Corp. 2011,2016. All Rights Reserved.
|
||||
// Copyright IBM Corp. 2011,2019. All Rights Reserved.
|
||||
// Node module: loopback-datasource-juggler
|
||||
// This file is licensed under the MIT License.
|
||||
// License text available at https://opensource.org/licenses/MIT
|
||||
|
||||
'use strict';
|
||||
|
||||
var Schema = require('../index').Schema;
|
||||
var Text = Schema.Text;
|
||||
const Schema = require('../index').Schema;
|
||||
const Text = Schema.Text;
|
||||
|
||||
var nbSchemaRequests = 0;
|
||||
let nbSchemaRequests = 0;
|
||||
|
||||
var batch;
|
||||
var schemaName;
|
||||
let batch;
|
||||
let schemaName;
|
||||
|
||||
function it(name, cases) {
|
||||
batch[schemaName][name] = cases;
|
||||
|
@ -27,7 +27,7 @@ module.exports = function testSchema(exportCasesHere, dataSource) {
|
|||
if (dataSource.name.match(/^\/.*\/test\/\.\.$/)) {
|
||||
schemaName = schemaName.split('/').slice(-3).shift();
|
||||
}
|
||||
var start;
|
||||
let start;
|
||||
|
||||
batch['should connect to database'] = function(test) {
|
||||
start = Date.now();
|
||||
|
@ -69,31 +69,11 @@ Object.defineProperty(module.exports, 'skip', {
|
|||
value: skip,
|
||||
});
|
||||
|
||||
function clearAndCreate(model, data, callback) {
|
||||
var createdItems = [];
|
||||
model.destroyAll(function() {
|
||||
nextItem(null, null);
|
||||
});
|
||||
|
||||
var itemIndex = 0;
|
||||
|
||||
function nextItem(err, lastItem) {
|
||||
if (lastItem !== null) {
|
||||
createdItems.push(lastItem);
|
||||
}
|
||||
if (itemIndex >= data.length) {
|
||||
callback(createdItems);
|
||||
return;
|
||||
}
|
||||
model.create(data[itemIndex], nextItem);
|
||||
itemIndex++;
|
||||
}
|
||||
}
|
||||
|
||||
/* eslint-disable mocha/handle-done-callback */
|
||||
function testOrm(dataSource) {
|
||||
var requestsAreCounted = dataSource.name !== 'mongodb';
|
||||
const requestsAreCounted = dataSource.name !== 'mongodb';
|
||||
|
||||
var Post, User, Passport, Log, Dog;
|
||||
let Post, User, Passport, Log, Dog;
|
||||
|
||||
it('should define class', function(test) {
|
||||
User = dataSource.define('User', {
|
||||
|
@ -122,7 +102,7 @@ function testOrm(dataSource) {
|
|||
extra: Object,
|
||||
});
|
||||
|
||||
var newuser = new User({settings: {hey: 'you'}});
|
||||
const newuser = new User({settings: {hey: 'you'}});
|
||||
test.ok(newuser.settings);
|
||||
|
||||
Post = dataSource.define('Post', {
|
||||
|
@ -176,7 +156,7 @@ function testOrm(dataSource) {
|
|||
Passport.belongsTo(User, {as: 'owner', foreignKey: 'ownerId'});
|
||||
User.hasMany(Passport, {as: 'passports', foreignKey: 'ownerId'});
|
||||
|
||||
var user = new User;
|
||||
const user = new User;
|
||||
|
||||
test.ok(User instanceof Function);
|
||||
|
||||
|
@ -198,7 +178,7 @@ function testOrm(dataSource) {
|
|||
});
|
||||
|
||||
it('should initialize object properly', function(test) {
|
||||
var hw = 'Hello word',
|
||||
const hw = 'Hello word',
|
||||
now = Date.now(),
|
||||
post = new Post({title: hw}),
|
||||
anotherPost = Post({title: 'Resig style constructor'});
|
||||
|
@ -217,7 +197,7 @@ function testOrm(dataSource) {
|
|||
});
|
||||
|
||||
it('should save object', function(test) {
|
||||
var title = 'Initial title', title2 = 'Hello world',
|
||||
const title = 'Initial title', title2 = 'Hello world',
|
||||
date = new Date;
|
||||
|
||||
Post.create({
|
||||
|
@ -233,7 +213,51 @@ function testOrm(dataSource) {
|
|||
test.equal(obj.title, title2);
|
||||
test.ok(!obj.propertyChanged('title'));
|
||||
|
||||
var p = new Post({title: 1});
|
||||
const p = new Post({title: 1});
|
||||
p.title = 2;
|
||||
p.save(function(err, obj) {
|
||||
test.ok(!p.propertyChanged('title'));
|
||||
p.title = 3;
|
||||
test.ok(p.propertyChanged('title'));
|
||||
test.equal(p.title_was, 2);
|
||||
p.save(function() {
|
||||
test.equal(p.title_was, 3);
|
||||
test.ok(!p.propertyChanged('title'));
|
||||
test.done();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('should save objects when createAll is invoked', function(test) {
|
||||
const title = 'Initial title',
|
||||
title2 = 'Hello world',
|
||||
date = new Date();
|
||||
|
||||
Post.createAll([
|
||||
{
|
||||
title,
|
||||
date,
|
||||
},
|
||||
{
|
||||
title: 'Title 2',
|
||||
date: date,
|
||||
},
|
||||
], function(err, objs) {
|
||||
const obj = objs[0];
|
||||
test.ok(obj.id, 'Object id should present');
|
||||
test.ok(objs[1].id, 'Object id should present');
|
||||
test.equals(obj.title, title);
|
||||
test.equals(objs[1].title, 'Title 2');
|
||||
// test.equals(obj.date, date);
|
||||
obj.title = title2;
|
||||
test.ok(obj.propertyChanged('title'), 'Title changed');
|
||||
obj.save(function(err, obj) {
|
||||
test.equal(obj.title, title2);
|
||||
test.ok(!obj.propertyChanged('title'));
|
||||
|
||||
const p = new Post({title: 1});
|
||||
p.title = 2;
|
||||
p.save(function(err, obj) {
|
||||
test.ok(!p.propertyChanged('title'));
|
||||
|
@ -251,7 +275,7 @@ function testOrm(dataSource) {
|
|||
});
|
||||
|
||||
it('should create object with initial data', function(test) {
|
||||
var title = 'Initial title',
|
||||
const title = 'Initial title',
|
||||
date = new Date;
|
||||
|
||||
Post.create({
|
||||
|
@ -300,8 +324,8 @@ function testOrm(dataSource) {
|
|||
*/
|
||||
|
||||
it('should not re-instantiate object on saving', function(test) {
|
||||
var title = 'Initial title';
|
||||
var post = new Post({title: title});
|
||||
const title = 'Initial title';
|
||||
const post = new Post({title: title});
|
||||
post.save(function(err, savedPost) {
|
||||
test.strictEqual(post, savedPost);
|
||||
test.done();
|
||||
|
@ -328,21 +352,21 @@ function testOrm(dataSource) {
|
|||
});
|
||||
|
||||
it('should handle virtual attributes', function(test) {
|
||||
var salt = 's0m3s3cr3t5a1t';
|
||||
const salt = 's0m3s3cr3t5a1t';
|
||||
|
||||
User.setter.passwd = function(password) {
|
||||
this._passwd = calcHash(password, salt);
|
||||
};
|
||||
|
||||
function calcHash(pass, salt) {
|
||||
var crypto = require('crypto');
|
||||
var hash = crypto.createHash('sha256');
|
||||
const crypto = require('crypto');
|
||||
const hash = crypto.createHash('sha256');
|
||||
hash.update(pass);
|
||||
hash.update(salt);
|
||||
return hash.digest('base64');
|
||||
}
|
||||
|
||||
var u = new User;
|
||||
const u = new User;
|
||||
u.passwd = 's3cr3t';
|
||||
test.equal(u.passwd, calcHash('s3cr3t', salt));
|
||||
test.done();
|
||||
|
@ -379,7 +403,7 @@ function testOrm(dataSource) {
|
|||
});
|
||||
});
|
||||
|
||||
var countOfposts, countOfpostsFiltered;
|
||||
let countOfposts, countOfpostsFiltered;
|
||||
it('should fetch collection', function(test) {
|
||||
Post.all(function(err, posts) {
|
||||
countOfposts = posts.length;
|
||||
|
@ -393,7 +417,7 @@ function testOrm(dataSource) {
|
|||
});
|
||||
|
||||
it('should find records filtered with multiple attributes', function(test) {
|
||||
var d = new Date;
|
||||
const d = new Date;
|
||||
Post.create({title: 'title', content: 'content', published: true, date: d}, function(err, post) {
|
||||
Post.all({where: {title: 'title', date: d, published: true}}, function(err, res) {
|
||||
test.equals(res.length, 1, 'Filtering Posts returns one post');
|
||||
|
@ -407,7 +431,7 @@ function testOrm(dataSource) {
|
|||
dataSource.name !== 'memory' &&
|
||||
dataSource.name !== 'neo4j' &&
|
||||
dataSource.name !== 'cradle'
|
||||
)
|
||||
)
|
||||
it('relations key is working', function(test) {
|
||||
test.ok(User.relations, 'Relations key should be defined');
|
||||
test.ok(User.relations.posts, 'posts relation should exist on User');
|
||||
|
@ -484,8 +508,8 @@ function testOrm(dataSource) {
|
|||
// Finding one post with an existing author associated
|
||||
Post.all(function(err, posts) {
|
||||
// We try to get the first post with a userId != NULL
|
||||
for (var i = 0; i < posts.length; i++) {
|
||||
var post = posts[i];
|
||||
for (let i = 0; i < posts.length; i++) {
|
||||
const post = posts[i];
|
||||
if (post.userId) {
|
||||
// We could get the user with belongs to relationship but it is better if there is no interactions.
|
||||
User.findById(post.userId, function(err, user) {
|
||||
|
@ -494,7 +518,7 @@ function testOrm(dataSource) {
|
|||
// There can't be any concurrency because we are counting requests
|
||||
// We are first testing cases when user has posts
|
||||
user.posts(function(err, data) {
|
||||
var nbInitialRequests = nbSchemaRequests;
|
||||
const nbInitialRequests = nbSchemaRequests;
|
||||
user.posts(function(err, data2) {
|
||||
test.equal(data.length, 2, 'There should be 2 posts.');
|
||||
test.equal(data.length, data2.length, 'Posts should be the same, since we are loading on the same object.');
|
||||
|
@ -513,7 +537,7 @@ function testOrm(dataSource) {
|
|||
|
||||
// We are now testing cases when user doesn't have any post
|
||||
voidUser.posts(function(err, data) {
|
||||
var nbInitialRequests = nbSchemaRequests;
|
||||
const nbInitialRequests = nbSchemaRequests;
|
||||
voidUser.posts(function(err, data2) {
|
||||
test.equal(data.length, 0, 'There shouldn\'t be any posts (1/2).');
|
||||
test.equal(data2.length, 0, 'There shouldn\'t be any posts (2/2).');
|
||||
|
@ -549,13 +573,13 @@ function testOrm(dataSource) {
|
|||
// });
|
||||
|
||||
it('should support scopes', function(test) {
|
||||
var wait = 2;
|
||||
let wait = 2;
|
||||
|
||||
test.ok(Post.scope, 'Scope supported');
|
||||
Post.scope('published', {where: {published: true}});
|
||||
test.ok(typeof Post.published === 'function');
|
||||
test.ok(Post.published._scope.where.published === true);
|
||||
var post = Post.published.build();
|
||||
const post = Post.published.build();
|
||||
test.ok(post.published, 'Can build');
|
||||
test.ok(post.isNewRecord());
|
||||
Post.published.create(function(err, psto) {
|
||||
|
@ -576,20 +600,20 @@ function testOrm(dataSource) {
|
|||
|
||||
function done() {
|
||||
if (--wait === 0) test.done();
|
||||
};
|
||||
}
|
||||
});
|
||||
|
||||
it('should return type of property', function(test) {
|
||||
test.equal(Post.getPropertyType('title'), 'String');
|
||||
test.equal(Post.getPropertyType('content'), 'Text');
|
||||
var p = new Post;
|
||||
const p = new Post;
|
||||
test.equal(p.getPropertyType('title'), 'String');
|
||||
test.equal(p.getPropertyType('content'), 'Text');
|
||||
test.done();
|
||||
});
|
||||
|
||||
it('should handle ORDER clause', function(test) {
|
||||
var titles = [
|
||||
const titles = [
|
||||
{title: 'Title A', subject: 'B'},
|
||||
{title: 'Title Z', subject: 'A'},
|
||||
{title: 'Title M', subject: 'C'},
|
||||
|
@ -597,8 +621,8 @@ function testOrm(dataSource) {
|
|||
{title: 'Title B', subject: 'A'},
|
||||
{title: 'Title C', subject: 'D'},
|
||||
];
|
||||
var isRedis = Post.dataSource.name === 'redis';
|
||||
var dates = isRedis ? [5, 9, 0, 17, 10, 9] : [
|
||||
const isRedis = Post.dataSource.name === 'redis';
|
||||
const dates = isRedis ? [5, 9, 0, 17, 10, 9] : [
|
||||
new Date(1000 * 5),
|
||||
new Date(1000 * 9),
|
||||
new Date(1000 * 0),
|
||||
|
@ -610,7 +634,7 @@ function testOrm(dataSource) {
|
|||
Post.create({title: t.title, subject: t.subject, date: dates[i]}, done);
|
||||
});
|
||||
|
||||
var i = 0, tests = 0;
|
||||
let i = 0, tests = 0;
|
||||
|
||||
function done(err, obj) {
|
||||
if (++i === titles.length) {
|
||||
|
@ -716,7 +740,7 @@ function testOrm(dataSource) {
|
|||
});
|
||||
}
|
||||
|
||||
var fin = 0;
|
||||
let fin = 0;
|
||||
|
||||
function finished() {
|
||||
if (++fin === tests) {
|
||||
|
@ -870,8 +894,8 @@ function testOrm(dataSource) {
|
|||
// });
|
||||
|
||||
it('should handle order clause with direction', function(test) {
|
||||
var wait = 0;
|
||||
var emails = [
|
||||
let wait = 0;
|
||||
const emails = [
|
||||
'john@hcompany.com',
|
||||
'tom@hcompany.com',
|
||||
'admin@hcompany.com',
|
||||
|
@ -886,7 +910,7 @@ function testOrm(dataSource) {
|
|||
User.create({email: email, name: 'Nick'}, done);
|
||||
});
|
||||
});
|
||||
var tests = 2;
|
||||
let tests = 2;
|
||||
|
||||
function done() {
|
||||
process.nextTick(function() {
|
||||
|
@ -899,7 +923,7 @@ function testOrm(dataSource) {
|
|||
|
||||
function doSortTest() {
|
||||
User.all({order: 'email ASC', where: {name: 'Nick'}}, function(err, users) {
|
||||
var _emails = emails.sort();
|
||||
const _emails = emails.sort();
|
||||
users.forEach(function(user, i) {
|
||||
test.equal(_emails[i], user.email, 'ASC sorting');
|
||||
});
|
||||
|
@ -909,7 +933,7 @@ function testOrm(dataSource) {
|
|||
|
||||
function doReverseSortTest() {
|
||||
User.all({order: 'email DESC', where: {name: 'Nick'}}, function(err, users) {
|
||||
var _emails = emails.sort().reverse();
|
||||
const _emails = emails.sort().reverse();
|
||||
users.forEach(function(user, i) {
|
||||
test.equal(_emails[i], user.email, 'DESC sorting');
|
||||
});
|
||||
|
@ -924,7 +948,7 @@ function testOrm(dataSource) {
|
|||
|
||||
it('should return id in find result even after updateAttributes', function(test) {
|
||||
Post.create(function(err, post) {
|
||||
var id = post.id;
|
||||
const id = post.id;
|
||||
test.ok(post.published === false);
|
||||
post.updateAttributes({title: 'hey', published: true}, function() {
|
||||
Post.find(id, function(err, post) {
|
||||
|
@ -937,7 +961,7 @@ function testOrm(dataSource) {
|
|||
});
|
||||
|
||||
it('should handle belongsTo correctly', function(test) {
|
||||
var passport = new Passport({ownerId: 16});
|
||||
const passport = new Passport({ownerId: 16});
|
||||
// sync getter
|
||||
test.equal(passport.owner(), 16);
|
||||
// sync setter
|
||||
|
@ -1016,7 +1040,7 @@ function testOrm(dataSource) {
|
|||
|
||||
if (dataSource.name !== 'mongoose' && dataSource.name !== 'neo4j')
|
||||
it('should update or create record', function(test) {
|
||||
var newData = {
|
||||
const newData = {
|
||||
id: 1,
|
||||
title: 'New title (really new)',
|
||||
content: 'Some example content (updated)',
|
||||
|
@ -1057,7 +1081,7 @@ function testOrm(dataSource) {
|
|||
User.setter.passwd = function(pass) {
|
||||
this._passwd = pass + 'salt';
|
||||
};
|
||||
var u = new User({passwd: 'qwerty'});
|
||||
const u = new User({passwd: 'qwerty'});
|
||||
test.equal(u.passwd, 'qwertysalt');
|
||||
u.save(function(err, user) {
|
||||
User.findById(user.id, function(err, user) {
|
||||
|
@ -1082,10 +1106,10 @@ function testOrm(dataSource) {
|
|||
});
|
||||
|
||||
it('should work with typed and untyped nested collections', function(test) {
|
||||
var post = new Post;
|
||||
var like = post.likes.push({foo: 'bar'});
|
||||
const post = new Post;
|
||||
const like = post.likes.push({foo: 'bar'});
|
||||
test.equal(like.constructor.name, 'ListItem');
|
||||
var related = post.related.push({hello: 'world'});
|
||||
const related = post.related.push({hello: 'world'});
|
||||
test.ok(related.someMethod);
|
||||
post.save(function(err, p) {
|
||||
test.equal(p.likes.nextid, 2);
|
||||
|
@ -1118,7 +1142,7 @@ function testOrm(dataSource) {
|
|||
});
|
||||
|
||||
it('should find or create', function(test) {
|
||||
var email = 'some email ' + Math.random();
|
||||
const email = 'some email ' + Math.random();
|
||||
User.findOrCreate({where: {email: email}}, function(err, u, created) {
|
||||
test.ok(u);
|
||||
test.ok(!u.age);
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// Copyright IBM Corp. 2015,2016. All Rights Reserved.
|
||||
// Copyright IBM Corp. 2015,2019. All Rights Reserved.
|
||||
// Node module: loopback-datasource-juggler
|
||||
// This file is licensed under the MIT License.
|
||||
// License text available at https://opensource.org/licenses/MIT
|
||||
|
@ -7,9 +7,9 @@
|
|||
'use strict';
|
||||
|
||||
/* global getSchema:false */
|
||||
var should = require('./init.js');
|
||||
var async = require('async');
|
||||
var db, User, options, filter;
|
||||
const should = require('./init.js');
|
||||
const async = require('async');
|
||||
let db, User, options, filter;
|
||||
|
||||
describe('crud-with-options', function() {
|
||||
before(function(done) {
|
||||
|
@ -23,6 +23,7 @@ describe('crud-with-options', function() {
|
|||
role: {type: String, index: true},
|
||||
order: {type: Number, index: true, sort: true},
|
||||
vip: {type: Boolean},
|
||||
address: {type: {city: String, area: String}},
|
||||
});
|
||||
options = {};
|
||||
filter = {fields: ['name', 'id']};
|
||||
|
@ -89,7 +90,8 @@ describe('crud-with-options', function() {
|
|||
function(done) {
|
||||
User.findById(undefined, {}, function(err, u) {
|
||||
err.should.be.eql(
|
||||
new Error('Model::findById requires the id argument'));
|
||||
new Error('Model::findById requires the id argument'),
|
||||
);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
@ -144,7 +146,7 @@ describe('crud-with-options', function() {
|
|||
|
||||
it('should allow promise-style findById',
|
||||
function(done) {
|
||||
User.create({name: 'w', email: 'w@y.com'}).then(function(u) {
|
||||
User.create({id: 15, name: 'w', email: 'w@y.com'}).then(function(u) {
|
||||
should.exist(u.id);
|
||||
return User.findById(u.id).then(function(u) {
|
||||
should.exist(u);
|
||||
|
@ -191,7 +193,7 @@ describe('crud-with-options', function() {
|
|||
|
||||
describe('findByIds', function() {
|
||||
before(function(done) {
|
||||
var people = [
|
||||
const people = [
|
||||
{id: 1, name: 'a', vip: true},
|
||||
{id: 2, name: 'b'},
|
||||
{id: 3, name: 'c'},
|
||||
|
@ -211,7 +213,7 @@ describe('crud-with-options', function() {
|
|||
User.findByIds([3, 2, 1], function(err, users) {
|
||||
should.exist(users);
|
||||
should.not.exist(err);
|
||||
var names = users.map(function(u) { return u.name; });
|
||||
const names = users.map(function(u) { return u.name; });
|
||||
names.should.eql(['c', 'b', 'a']);
|
||||
done();
|
||||
});
|
||||
|
@ -223,7 +225,7 @@ describe('crud-with-options', function() {
|
|||
{where: {vip: true}}, options, function(err, users) {
|
||||
should.exist(users);
|
||||
should.not.exist(err);
|
||||
var names = users.map(function(u) {
|
||||
const names = users.map(function(u) {
|
||||
return u.name;
|
||||
});
|
||||
names.should.eql(['d', 'a']);
|
||||
|
@ -405,15 +407,15 @@ describe('crud-with-options', function() {
|
|||
|
||||
describe('save', function() {
|
||||
it('should allow save(options, cb)', function(done) {
|
||||
var options = {foo: 'bar'};
|
||||
var opts;
|
||||
const options = {foo: 'bar'};
|
||||
let opts;
|
||||
|
||||
User.observe('after save', function(ctx, next) {
|
||||
opts = ctx.options;
|
||||
next();
|
||||
});
|
||||
|
||||
var u = new User();
|
||||
const u = new User();
|
||||
u.save(options, function(err) {
|
||||
should.not.exist(err);
|
||||
options.should.equal(opts);
|
||||
|
@ -519,6 +521,24 @@ describe('crud-with-options', function() {
|
|||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('updateAttributes', function() {
|
||||
beforeEach(seed);
|
||||
it('preserves document properties not modified by the patch', function() {
|
||||
return User.findOne({where: {name: 'John Lennon'}})
|
||||
.then(function(user) {
|
||||
return user.updateAttributes({address: {city: 'Volos'}});
|
||||
})
|
||||
.then(function() {
|
||||
return User.findOne({where: {name: 'John Lennon'}}); // retrieve the user again from the db
|
||||
})
|
||||
.then(function(updatedUser) {
|
||||
updatedUser.address.city.should.equal('Volos');
|
||||
should(updatedUser.address.area).not.be.exactly(null);
|
||||
should(updatedUser.address.area).be.undefined();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('upsertWithWhere', function() {
|
||||
|
@ -585,8 +605,9 @@ describe('upsertWithWhere', function() {
|
|||
});
|
||||
|
||||
function seed(done) {
|
||||
var beatles = [
|
||||
const beatles = [
|
||||
{
|
||||
id: 0,
|
||||
seq: 0,
|
||||
name: 'John Lennon',
|
||||
email: 'john@b3atl3s.co.uk',
|
||||
|
@ -596,6 +617,7 @@ function seed(done) {
|
|||
vip: true,
|
||||
},
|
||||
{
|
||||
id: 1,
|
||||
seq: 1,
|
||||
name: 'Paul McCartney',
|
||||
email: 'paul@b3atl3s.co.uk',
|
||||
|
@ -604,16 +626,19 @@ function seed(done) {
|
|||
order: 1,
|
||||
vip: true,
|
||||
},
|
||||
{seq: 2, name: 'George Harrison', order: 5, vip: false},
|
||||
{seq: 3, name: 'Ringo Starr', order: 6, vip: false},
|
||||
{seq: 4, name: 'Pete Best', order: 4},
|
||||
{seq: 5, name: 'Stuart Sutcliffe', order: 3, vip: true},
|
||||
{id: 2, seq: 2, name: 'George Harrison', order: 5, vip: false},
|
||||
{id: 3, seq: 3, name: 'Ringo Starr', order: 6, vip: false},
|
||||
{id: 4, seq: 4, name: 'Pete Best', order: 4},
|
||||
{id: 5, seq: 5, name: 'Stuart Sutcliffe', order: 3, vip: true},
|
||||
];
|
||||
|
||||
async.series([
|
||||
User.destroyAll.bind(User),
|
||||
function(cb) {
|
||||
async.each(beatles, User.create.bind(User), cb);
|
||||
},
|
||||
], done);
|
||||
async.series(
|
||||
[
|
||||
User.destroyAll.bind(User),
|
||||
function(cb) {
|
||||
User.createAll(beatles, cb);
|
||||
},
|
||||
],
|
||||
done,
|
||||
);
|
||||
}
|
||||
|
|
|
@ -1,16 +1,25 @@
|
|||
// Copyright IBM Corp. 2016. All Rights Reserved.
|
||||
// Copyright IBM Corp. 2016,2019. All Rights Reserved.
|
||||
// Node module: loopback-datasource-juggler
|
||||
// This file is licensed under the MIT License.
|
||||
// License text available at https://opensource.org/licenses/MIT
|
||||
|
||||
'use strict';
|
||||
|
||||
var should = require('./init.js');
|
||||
var DataSource = require('../lib/datasource.js').DataSource;
|
||||
const should = require('./init.js');
|
||||
const DataSource = require('../lib/datasource.js').DataSource;
|
||||
|
||||
describe('DataSource', function() {
|
||||
it('clones settings to prevent surprising changes in passed args', () => {
|
||||
const config = {connector: 'memory'};
|
||||
|
||||
const ds = new DataSource(config);
|
||||
ds.settings.extra = true;
|
||||
|
||||
config.should.eql({connector: 'memory'});
|
||||
});
|
||||
|
||||
it('reports helpful error when connector init throws', function() {
|
||||
var throwingConnector = {
|
||||
const throwingConnector = {
|
||||
name: 'loopback-connector-throwing',
|
||||
initialize: function(ds, cb) {
|
||||
throw new Error('expected test error');
|
||||
|
@ -50,7 +59,7 @@ describe('DataSource', function() {
|
|||
* new DataSource(dsName, settings) without settings.name
|
||||
*/
|
||||
it('should retain the name assigned to it', function() {
|
||||
var dataSource = new DataSource('myDataSource', {
|
||||
const dataSource = new DataSource('myDataSource', {
|
||||
connector: 'memory',
|
||||
});
|
||||
|
||||
|
@ -61,7 +70,7 @@ describe('DataSource', function() {
|
|||
* new DataSource(dsName, settings)
|
||||
*/
|
||||
it('should allow the name assigned to it to take precedence over the settings name', function() {
|
||||
var dataSource = new DataSource('myDataSource', {
|
||||
const dataSource = new DataSource('myDataSource', {
|
||||
name: 'defaultDataSource',
|
||||
connector: 'memory',
|
||||
});
|
||||
|
@ -73,7 +82,7 @@ describe('DataSource', function() {
|
|||
* new DataSource(settings) with settings.name
|
||||
*/
|
||||
it('should retain the name from the settings if no name is assigned', function() {
|
||||
var dataSource = new DataSource({
|
||||
const dataSource = new DataSource({
|
||||
name: 'defaultDataSource',
|
||||
connector: 'memory',
|
||||
});
|
||||
|
@ -85,7 +94,7 @@ describe('DataSource', function() {
|
|||
* new DataSource(undefined, settings)
|
||||
*/
|
||||
it('should retain the name from the settings if name is undefined', function() {
|
||||
var dataSource = new DataSource(undefined, {
|
||||
const dataSource = new DataSource(undefined, {
|
||||
name: 'defaultDataSource',
|
||||
connector: 'memory',
|
||||
});
|
||||
|
@ -97,7 +106,7 @@ describe('DataSource', function() {
|
|||
* new DataSource(settings) without settings.name
|
||||
*/
|
||||
it('should use the connector name if no name is provided', function() {
|
||||
var dataSource = new DataSource({
|
||||
const dataSource = new DataSource({
|
||||
connector: 'memory',
|
||||
});
|
||||
|
||||
|
@ -108,14 +117,14 @@ describe('DataSource', function() {
|
|||
* new DataSource(connectorInstance)
|
||||
*/
|
||||
it('should accept resolved connector', function() {
|
||||
var mockConnector = {
|
||||
const mockConnector = {
|
||||
name: 'loopback-connector-mock',
|
||||
initialize: function(ds, cb) {
|
||||
ds.connector = mockConnector;
|
||||
return cb(null);
|
||||
},
|
||||
};
|
||||
var dataSource = new DataSource(mockConnector);
|
||||
const dataSource = new DataSource(mockConnector);
|
||||
|
||||
dataSource.name.should.equal('loopback-connector-mock');
|
||||
dataSource.connector.should.equal(mockConnector);
|
||||
|
@ -124,15 +133,15 @@ describe('DataSource', function() {
|
|||
/**
|
||||
* new DataSource(dsName, connectorInstance)
|
||||
*/
|
||||
it('should accept resolved connector', function() {
|
||||
var mockConnector = {
|
||||
it('should accept dsName and resolved connector', function() {
|
||||
const mockConnector = {
|
||||
name: 'loopback-connector-mock',
|
||||
initialize: function(ds, cb) {
|
||||
ds.connector = mockConnector;
|
||||
return cb(null);
|
||||
},
|
||||
};
|
||||
var dataSource = new DataSource('myDataSource', mockConnector);
|
||||
const dataSource = new DataSource('myDataSource', mockConnector);
|
||||
|
||||
dataSource.name.should.equal('myDataSource');
|
||||
dataSource.connector.should.equal(mockConnector);
|
||||
|
@ -142,19 +151,210 @@ describe('DataSource', function() {
|
|||
* new DataSource(connectorInstance, settings)
|
||||
*/
|
||||
it('should accept resolved connector and settings', function() {
|
||||
var mockConnector = {
|
||||
const mockConnector = {
|
||||
name: 'loopback-connector-mock',
|
||||
initialize: function(ds, cb) {
|
||||
ds.connector = mockConnector;
|
||||
return cb(null);
|
||||
},
|
||||
};
|
||||
var dataSource = new DataSource(mockConnector, {name: 'myDataSource'});
|
||||
const dataSource = new DataSource(mockConnector, {name: 'myDataSource'});
|
||||
|
||||
dataSource.name.should.equal('myDataSource');
|
||||
dataSource.connector.should.equal(mockConnector);
|
||||
});
|
||||
|
||||
it('should set states correctly with eager connect', function(done) {
|
||||
const mockConnector = {
|
||||
name: 'loopback-connector-mock',
|
||||
initialize: function(ds, cb) {
|
||||
ds.connector = mockConnector;
|
||||
this.connect(cb);
|
||||
},
|
||||
|
||||
connect: function(cb) {
|
||||
process.nextTick(function() {
|
||||
cb(null);
|
||||
});
|
||||
},
|
||||
};
|
||||
const dataSource = new DataSource(mockConnector);
|
||||
// DataSource is instantiated
|
||||
// connected: false, connecting: false, initialized: false
|
||||
dataSource.connected.should.be.false();
|
||||
dataSource.connecting.should.be.false();
|
||||
dataSource.initialized.should.be.false();
|
||||
|
||||
dataSource.on('initialized', function() {
|
||||
// DataSource is initialized with lazyConnect
|
||||
// connected: false, connecting: false, initialized: true
|
||||
dataSource.connected.should.be.false();
|
||||
dataSource.connecting.should.be.false();
|
||||
dataSource.initialized.should.be.true();
|
||||
});
|
||||
|
||||
dataSource.on('connected', function() {
|
||||
// DataSource is now connected
|
||||
// connected: true, connecting: false
|
||||
dataSource.connected.should.be.true();
|
||||
dataSource.connecting.should.be.false();
|
||||
});
|
||||
|
||||
// Call connect() in next tick so that we'll receive initialized event
|
||||
// first
|
||||
process.nextTick(function() {
|
||||
// At this point, the datasource is already connected by
|
||||
// connector's (mockConnector) initialize function
|
||||
dataSource.connect(function() {
|
||||
// DataSource is now connected
|
||||
// connected: true, connecting: false
|
||||
dataSource.connected.should.be.true();
|
||||
dataSource.connecting.should.be.false();
|
||||
done();
|
||||
});
|
||||
// As the datasource is already connected, no connecting will happen
|
||||
// connected: true, connecting: false
|
||||
dataSource.connected.should.be.true();
|
||||
dataSource.connecting.should.be.false();
|
||||
});
|
||||
});
|
||||
|
||||
it('should set states correctly with deferred connect', function(done) {
|
||||
const mockConnector = {
|
||||
name: 'loopback-connector-mock',
|
||||
initialize: function(ds, cb) {
|
||||
ds.connector = mockConnector;
|
||||
// Explicitly call back with false to denote connection is not ready
|
||||
process.nextTick(function() {
|
||||
cb(null, false);
|
||||
});
|
||||
},
|
||||
|
||||
connect: function(cb) {
|
||||
process.nextTick(function() {
|
||||
cb(null);
|
||||
});
|
||||
},
|
||||
};
|
||||
const dataSource = new DataSource(mockConnector);
|
||||
// DataSource is instantiated
|
||||
// connected: false, connecting: false, initialized: false
|
||||
dataSource.connected.should.be.false();
|
||||
dataSource.connecting.should.be.false();
|
||||
dataSource.initialized.should.be.false();
|
||||
|
||||
dataSource.on('initialized', function() {
|
||||
// DataSource is initialized with lazyConnect
|
||||
// connected: false, connecting: false, initialized: true
|
||||
dataSource.connected.should.be.false();
|
||||
dataSource.connecting.should.be.false();
|
||||
dataSource.initialized.should.be.true();
|
||||
});
|
||||
|
||||
dataSource.on('connected', function() {
|
||||
// DataSource is now connected
|
||||
// connected: true, connecting: false
|
||||
dataSource.connected.should.be.true();
|
||||
dataSource.connecting.should.be.false();
|
||||
});
|
||||
|
||||
// Call connect() in next tick so that we'll receive initialized event
|
||||
// first
|
||||
process.nextTick(function() {
|
||||
dataSource.connect(function() {
|
||||
// DataSource is now connected
|
||||
// connected: true, connecting: false
|
||||
dataSource.connected.should.be.true();
|
||||
dataSource.connecting.should.be.false();
|
||||
done();
|
||||
});
|
||||
// As the datasource is not connected, connecting will happen
|
||||
// connected: false, connecting: true
|
||||
dataSource.connected.should.be.false();
|
||||
dataSource.connecting.should.be.true();
|
||||
});
|
||||
});
|
||||
|
||||
it('should set states correctly with lazyConnect = true', function(done) {
|
||||
const mockConnector = {
|
||||
name: 'loopback-connector-mock',
|
||||
initialize: function(ds, cb) {
|
||||
ds.connector = mockConnector;
|
||||
process.nextTick(function() {
|
||||
cb(null);
|
||||
});
|
||||
},
|
||||
|
||||
connect: function(cb) {
|
||||
process.nextTick(function() {
|
||||
cb(null);
|
||||
});
|
||||
},
|
||||
};
|
||||
const dataSource = new DataSource(mockConnector, {lazyConnect: true});
|
||||
// DataSource is instantiated
|
||||
// connected: false, connecting: false, initialized: false
|
||||
dataSource.connected.should.be.false();
|
||||
dataSource.connecting.should.be.false();
|
||||
dataSource.initialized.should.be.false();
|
||||
|
||||
dataSource.on('initialized', function() {
|
||||
// DataSource is initialized with lazyConnect
|
||||
// connected: false, connecting: false, initialized: true
|
||||
dataSource.connected.should.be.false();
|
||||
dataSource.connecting.should.be.false();
|
||||
dataSource.initialized.should.be.true();
|
||||
});
|
||||
|
||||
dataSource.on('connected', function() {
|
||||
// DataSource is now connected
|
||||
// connected: true, connecting: false
|
||||
dataSource.connected.should.be.true();
|
||||
dataSource.connecting.should.be.false();
|
||||
});
|
||||
|
||||
// Call connect() in next tick so that we'll receive initialized event
|
||||
// first
|
||||
process.nextTick(function() {
|
||||
dataSource.connect(function() {
|
||||
// DataSource is now connected
|
||||
// connected: true, connecting: false
|
||||
dataSource.connected.should.be.true();
|
||||
dataSource.connecting.should.be.false();
|
||||
done();
|
||||
});
|
||||
// DataSource is now connecting
|
||||
// connected: false, connecting: true
|
||||
dataSource.connected.should.be.false();
|
||||
dataSource.connecting.should.be.true();
|
||||
});
|
||||
});
|
||||
|
||||
it('provides stop() API calling disconnect', function(done) {
|
||||
const mockConnector = {
|
||||
name: 'loopback-connector-mock',
|
||||
initialize: function(ds, cb) {
|
||||
ds.connector = mockConnector;
|
||||
process.nextTick(function() {
|
||||
cb(null);
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
const dataSource = new DataSource(mockConnector);
|
||||
dataSource.on('connected', function() {
|
||||
// DataSource is now connected
|
||||
// connected: true, connecting: false
|
||||
dataSource.connected.should.be.true();
|
||||
dataSource.connecting.should.be.false();
|
||||
|
||||
dataSource.stop(() => {
|
||||
dataSource.connected.should.be.false();
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('deleteModelByName()', () => {
|
||||
it('removes the model from ModelBuilder registry', () => {
|
||||
const ds = new DataSource('ds', {connector: 'memory'});
|
||||
|
@ -186,4 +386,292 @@ describe('DataSource', function() {
|
|||
.should.not.containEql('TestModel');
|
||||
});
|
||||
});
|
||||
|
||||
describe('execute', () => {
|
||||
let ds;
|
||||
beforeEach(() => ds = new DataSource('ds', {connector: 'memory'}));
|
||||
|
||||
it('calls connnector to execute the command', async () => {
|
||||
let called = 'not called';
|
||||
ds.connector.execute = function(command, args, options, callback) {
|
||||
called = {command, args, options};
|
||||
callback(null, 'a-result');
|
||||
};
|
||||
|
||||
const result = await ds.execute(
|
||||
'command',
|
||||
['arg1', 'arg2'],
|
||||
{'a-flag': 'a-value'},
|
||||
);
|
||||
|
||||
result.should.be.equal('a-result');
|
||||
called.should.be.eql({
|
||||
command: 'command',
|
||||
args: ['arg1', 'arg2'],
|
||||
options: {'a-flag': 'a-value'},
|
||||
});
|
||||
});
|
||||
|
||||
it('supports shorthand version (cmd)', async () => {
|
||||
let called = 'not called';
|
||||
ds.connector.execute = function(command, args, options, callback) {
|
||||
// copied from loopback-connector/lib/sql.js
|
||||
if (typeof args === 'function' && options === undefined && callback === undefined) {
|
||||
// execute(sql, callback)
|
||||
options = {};
|
||||
callback = args;
|
||||
args = [];
|
||||
}
|
||||
|
||||
called = {command, args, options};
|
||||
callback(null, 'a-result');
|
||||
};
|
||||
|
||||
const result = await ds.execute('command');
|
||||
result.should.be.equal('a-result');
|
||||
called.should.be.eql({
|
||||
command: 'command',
|
||||
args: [],
|
||||
options: {},
|
||||
});
|
||||
});
|
||||
|
||||
it('supports shorthand version (cmd, args)', async () => {
|
||||
let called = 'not called';
|
||||
ds.connector.execute = function(command, args, options, callback) {
|
||||
// copied from loopback-connector/lib/sql.js
|
||||
if (typeof options === 'function' && callback === undefined) {
|
||||
// execute(sql, params, callback)
|
||||
callback = options;
|
||||
options = {};
|
||||
}
|
||||
|
||||
called = {command, args, options};
|
||||
callback(null, 'a-result');
|
||||
};
|
||||
|
||||
await ds.execute('command', ['arg1', 'arg2']);
|
||||
called.should.be.eql({
|
||||
command: 'command',
|
||||
args: ['arg1', 'arg2'],
|
||||
options: {},
|
||||
});
|
||||
});
|
||||
|
||||
it('converts multiple callbacks arguments into a promise resolved with an array', async () => {
|
||||
ds.connector.execute = function() {
|
||||
const callback = arguments[arguments.length - 1];
|
||||
callback(null, 'result1', 'result2');
|
||||
};
|
||||
const result = await ds.execute('command');
|
||||
result.should.eql(['result1', 'result2']);
|
||||
});
|
||||
|
||||
it('allows args as object', async () => {
|
||||
let called = 'not called';
|
||||
ds.connector.execute = function(command, args, options, callback) {
|
||||
called = {command, args, options};
|
||||
callback();
|
||||
};
|
||||
|
||||
// See https://www.npmjs.com/package/loopback-connector-neo4j-graph
|
||||
const command = 'MATCH (u:User {email: {email}}) RETURN u';
|
||||
await ds.execute(command, {email: 'alice@example.com'}, {options: true});
|
||||
called.should.be.eql({
|
||||
command,
|
||||
args: {email: 'alice@example.com'},
|
||||
options: {options: true},
|
||||
});
|
||||
});
|
||||
|
||||
it('supports MongoDB version (collection, cmd, args, options)', async () => {
|
||||
let called = 'not called';
|
||||
ds.connector.execute = function(...params) {
|
||||
const callback = params.pop();
|
||||
called = params;
|
||||
callback(null, 'a-result');
|
||||
};
|
||||
|
||||
const result = await ds.execute(
|
||||
'collection',
|
||||
'command',
|
||||
['arg1', 'arg2'],
|
||||
{options: true},
|
||||
);
|
||||
|
||||
result.should.equal('a-result');
|
||||
called.should.be.eql([
|
||||
'collection',
|
||||
'command',
|
||||
['arg1', 'arg2'],
|
||||
{options: true},
|
||||
]);
|
||||
});
|
||||
|
||||
it('supports free-form version (...params)', async () => {
|
||||
let called = 'not called';
|
||||
ds.connector.execute = function(...params) {
|
||||
const callback = params.pop();
|
||||
called = params;
|
||||
callback(null, 'a-result');
|
||||
};
|
||||
|
||||
const result = await ds.execute(
|
||||
'arg1',
|
||||
'arg2',
|
||||
'arg3',
|
||||
'arg4',
|
||||
{options: true},
|
||||
);
|
||||
|
||||
result.should.equal('a-result');
|
||||
called.should.be.eql([
|
||||
'arg1',
|
||||
'arg2',
|
||||
'arg3',
|
||||
'arg4',
|
||||
{options: true},
|
||||
]);
|
||||
});
|
||||
|
||||
it('throws NOT_IMPLEMENTED when no connector is provided', () => {
|
||||
ds.connector = undefined;
|
||||
return ds.execute('command').should.be.rejectedWith({
|
||||
code: 'NOT_IMPLEMENTED',
|
||||
});
|
||||
});
|
||||
|
||||
it('throws NOT_IMPLEMENTED for connectors not implementing execute', () => {
|
||||
ds.connector.execute = undefined;
|
||||
return ds.execute('command').should.be.rejectedWith({
|
||||
code: 'NOT_IMPLEMENTED',
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('automigrate', () => {
|
||||
it('reports connection errors (immediate connect)', async () => {
|
||||
const dataSource = new DataSource({
|
||||
connector: givenConnectorFailingOnConnect(),
|
||||
});
|
||||
dataSource.define('MyModel');
|
||||
await dataSource.automigrate().should.be.rejectedWith(/test failure/);
|
||||
});
|
||||
|
||||
it('reports connection errors (lazy connect)', () => {
|
||||
const dataSource = new DataSource({
|
||||
connector: givenConnectorFailingOnConnect(),
|
||||
lazyConnect: true,
|
||||
});
|
||||
dataSource.define('MyModel');
|
||||
return dataSource.automigrate().should.be.rejectedWith(/test failure/);
|
||||
});
|
||||
|
||||
function givenConnectorFailingOnConnect() {
|
||||
return givenMockConnector({
|
||||
connect: function(cb) {
|
||||
process.nextTick(() => cb(new Error('test failure')));
|
||||
},
|
||||
automigrate: function(models, cb) {
|
||||
cb(new Error('automigrate should not have been called'));
|
||||
},
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
describe('autoupdate', () => {
|
||||
it('reports connection errors (immediate connect)', async () => {
|
||||
const dataSource = new DataSource({
|
||||
connector: givenConnectorFailingOnConnect(),
|
||||
});
|
||||
dataSource.define('MyModel');
|
||||
await dataSource.autoupdate().should.be.rejectedWith(/test failure/);
|
||||
});
|
||||
|
||||
it('reports connection errors (lazy connect)', () => {
|
||||
const dataSource = new DataSource({
|
||||
connector: givenConnectorFailingOnConnect(),
|
||||
lazyConnect: true,
|
||||
});
|
||||
dataSource.define('MyModel');
|
||||
return dataSource.autoupdate().should.be.rejectedWith(/test failure/);
|
||||
});
|
||||
|
||||
function givenConnectorFailingOnConnect() {
|
||||
return givenMockConnector({
|
||||
connect: function(cb) {
|
||||
process.nextTick(() => cb(new Error('test failure')));
|
||||
},
|
||||
autoupdate: function(models, cb) {
|
||||
cb(new Error('autoupdate should not have been called'));
|
||||
},
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
describe('deleteAllModels', () => {
|
||||
it('removes all model definitions', () => {
|
||||
const ds = new DataSource({connector: 'memory'});
|
||||
ds.define('Category');
|
||||
ds.define('Product');
|
||||
|
||||
Object.keys(ds.modelBuilder.definitions)
|
||||
.should.deepEqual(['Category', 'Product']);
|
||||
Object.keys(ds.modelBuilder.models)
|
||||
.should.deepEqual(['Category', 'Product']);
|
||||
Object.keys(ds.connector._models)
|
||||
.should.deepEqual(['Category', 'Product']);
|
||||
|
||||
ds.deleteAllModels();
|
||||
|
||||
Object.keys(ds.modelBuilder.definitions).should.be.empty();
|
||||
Object.keys(ds.modelBuilder.models).should.be.empty();
|
||||
Object.keys(ds.connector._models).should.be.empty();
|
||||
});
|
||||
|
||||
it('preserves the connector instance', () => {
|
||||
const ds = new DataSource({connector: 'memory'});
|
||||
const connector = ds.connector;
|
||||
ds.deleteAllModels();
|
||||
ds.connector.should.equal(connector);
|
||||
});
|
||||
});
|
||||
|
||||
describe('getMaxOfflineRequests', () => {
|
||||
let ds;
|
||||
beforeEach(() => ds = new DataSource('ds', {connector: 'memory'}));
|
||||
|
||||
it('sets the default maximum number of event listeners to 16', () => {
|
||||
ds.getMaxOfflineRequests().should.be.eql(16);
|
||||
});
|
||||
|
||||
it('uses provided number of listeners', () => {
|
||||
ds.settings.maxOfflineRequests = 17;
|
||||
ds.getMaxOfflineRequests().should.be.eql(17);
|
||||
});
|
||||
|
||||
it('throws an error if a non-number is provided for the max number of listeners', () => {
|
||||
ds.settings.maxOfflineRequests = '17';
|
||||
|
||||
(function() {
|
||||
return ds.getMaxOfflineRequests();
|
||||
}).should.throw('maxOfflineRequests must be a number');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
function givenMockConnector(props) {
|
||||
const connector = {
|
||||
name: 'loopback-connector-mock',
|
||||
initialize: function(ds, cb) {
|
||||
ds.connector = connector;
|
||||
if (ds.settings.lazyConnect) {
|
||||
cb(null, false);
|
||||
} else {
|
||||
connector.connect(cb);
|
||||
}
|
||||
},
|
||||
...props,
|
||||
};
|
||||
return connector;
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// Copyright IBM Corp. 2013,2016. All Rights Reserved.
|
||||
// Copyright IBM Corp. 2013,2019. All Rights Reserved.
|
||||
// Node module: loopback-datasource-juggler
|
||||
// This file is licensed under the MIT License.
|
||||
// License text available at https://opensource.org/licenses/MIT
|
||||
|
@ -7,15 +7,21 @@
|
|||
'use strict';
|
||||
|
||||
/* global getSchema:false */
|
||||
var should = require('./init.js');
|
||||
const should = require('./init.js');
|
||||
|
||||
var db, Model;
|
||||
let db, Model, modelWithDecimalArray, dateArrayModel, numArrayModel;
|
||||
|
||||
class NestedClass {
|
||||
constructor(roleName) {
|
||||
this.roleName = roleName;
|
||||
}
|
||||
}
|
||||
|
||||
describe('datatypes', function() {
|
||||
before(function(done) {
|
||||
db = getSchema();
|
||||
var Nested = db.define('Nested', {});
|
||||
var modelTableSchema = {
|
||||
const Nested = db.define('Nested', {});
|
||||
const modelTableSchema = {
|
||||
str: String,
|
||||
date: Date,
|
||||
num: Number,
|
||||
|
@ -23,14 +29,83 @@ describe('datatypes', function() {
|
|||
list: {type: [String]},
|
||||
arr: Array,
|
||||
nested: Nested,
|
||||
nestedClass: NestedClass,
|
||||
};
|
||||
Model = db.define('Model', modelTableSchema);
|
||||
db.automigrate(['Model'], done);
|
||||
// 'modelWithDecimalArray' is too long an identifier name for Oracle DB
|
||||
modelWithDecimalArray = db.define('modelWithDecArr', {
|
||||
randomReview: {
|
||||
type: [String],
|
||||
mongodb: {
|
||||
dataType: 'Decimal128',
|
||||
},
|
||||
},
|
||||
});
|
||||
dateArrayModel = db.define('dateArrayModel', {
|
||||
bunchOfDates: [Date],
|
||||
bunchOfOtherDates: {
|
||||
type: [Date],
|
||||
},
|
||||
});
|
||||
numArrayModel = db.define('numArrayModel', {
|
||||
bunchOfNums: [Number],
|
||||
});
|
||||
db.automigrate(['Model', 'modelWithDecArr', 'dateArrayModel', 'numArrayModel'], done);
|
||||
});
|
||||
|
||||
it('should resolve top-level "type" property correctly', function() {
|
||||
const Account = db.define('Account', {
|
||||
type: String,
|
||||
id: String,
|
||||
});
|
||||
Account.definition.properties.type.type.should.equal(String);
|
||||
});
|
||||
|
||||
it('should resolve "type" sub-property correctly', function() {
|
||||
const Account = db.define('Account', {
|
||||
item: {type: {
|
||||
itemname: {type: String},
|
||||
type: {type: String},
|
||||
}},
|
||||
});
|
||||
Account.definition.properties.item.type.should.not.equal(String);
|
||||
});
|
||||
it('should resolve array prop with connector specific metadata', function() {
|
||||
const props = modelWithDecimalArray.definition.properties;
|
||||
props.randomReview.type.should.deepEqual(Array(String));
|
||||
props.randomReview.mongodb.should.deepEqual({dataType: 'Decimal128'});
|
||||
});
|
||||
|
||||
it('should coerce array of dates from string', async () => {
|
||||
const dateVal = new Date('2019-02-21T12:00:00').toISOString();
|
||||
const created = await dateArrayModel.create({
|
||||
bunchOfDates: [dateVal,
|
||||
dateVal,
|
||||
dateVal],
|
||||
bunchOfOtherDates: [dateVal,
|
||||
dateVal,
|
||||
dateVal],
|
||||
});
|
||||
created.bunchOfDates[0].should.be.an.instanceOf(Date);
|
||||
created.bunchOfDates[0].should.deepEqual(new Date(dateVal));
|
||||
created.bunchOfOtherDates[0].should.be.an.instanceOf(Date);
|
||||
created.bunchOfOtherDates[0].should.deepEqual(new Date(dateVal));
|
||||
});
|
||||
|
||||
it('should coerce array of numbers from string', async () => {
|
||||
const dateVal = new Date('2019-02-21T12:00:00').toISOString();
|
||||
const created = await numArrayModel.create({
|
||||
bunchOfNums: ['1',
|
||||
'2',
|
||||
'3'],
|
||||
});
|
||||
created.bunchOfNums[0].should.be.an.instanceOf(Number);
|
||||
created.bunchOfNums[0].should.equal(1);
|
||||
});
|
||||
|
||||
it('should return 400 when property of type array is set to string value',
|
||||
function(done) {
|
||||
var myModel = db.define('myModel', {
|
||||
const myModel = db.define('myModel', {
|
||||
list: {type: ['object']},
|
||||
});
|
||||
|
||||
|
@ -42,7 +117,7 @@ describe('datatypes', function() {
|
|||
|
||||
it('should return 400 when property of type array is set to object value',
|
||||
function(done) {
|
||||
var myModel = db.define('myModel', {
|
||||
const myModel = db.define('myModel', {
|
||||
list: {type: ['object']},
|
||||
});
|
||||
|
||||
|
@ -53,7 +128,8 @@ describe('datatypes', function() {
|
|||
});
|
||||
|
||||
it('should keep types when get read data from db', function(done) {
|
||||
var d = new Date('2015-01-01T12:00:00'), id;
|
||||
const d = new Date('2015-01-01T12:00:00');
|
||||
let id;
|
||||
|
||||
Model.create({
|
||||
str: 'hello', date: d, num: '3', bool: 1, list: ['test'], arr: [1, 'str'],
|
||||
|
@ -100,8 +176,194 @@ describe('datatypes', function() {
|
|||
}
|
||||
});
|
||||
|
||||
it('should create nested object defined by a class when reading data from db', async () => {
|
||||
const d = new Date('2015-01-01T12:00:00');
|
||||
let id;
|
||||
const created = await Model.create({
|
||||
date: d,
|
||||
list: ['test'],
|
||||
arr: [1, 'str'],
|
||||
nestedClass: new NestedClass('admin'),
|
||||
});
|
||||
created.list.toJSON().should.deepEqual(['test']);
|
||||
created.arr.toJSON().should.deepEqual([1, 'str']);
|
||||
created.date.should.be.an.instanceOf(Date);
|
||||
created.date.toString().should.equal(d.toString(), 'Time must match');
|
||||
created.nestedClass.should.have.property('roleName', 'admin');
|
||||
|
||||
const found = await Model.findById(created.id);
|
||||
should.exist(found);
|
||||
found.list.toJSON().should.deepEqual(['test']);
|
||||
found.arr.toJSON().should.deepEqual([1, 'str']);
|
||||
found.date.should.be.an.instanceOf(Date);
|
||||
found.date.toString().should.equal(d.toString(), 'Time must match');
|
||||
found.nestedClass.should.have.property('roleName', 'admin');
|
||||
});
|
||||
|
||||
it('should create nested object defined by a class using createAll', async () => {
|
||||
const d = new Date('2015-01-01T12:00:00');
|
||||
let id;
|
||||
const [created] = await Model.createAll([
|
||||
{
|
||||
date: d,
|
||||
list: ['test'],
|
||||
arr: [1, 'str'],
|
||||
nestedClass: new NestedClass('admin'),
|
||||
},
|
||||
]);
|
||||
created.list.toJSON().should.deepEqual(['test']);
|
||||
created.arr.toJSON().should.deepEqual([1, 'str']);
|
||||
created.date.should.be.an.instanceOf(Date);
|
||||
created.date.toString().should.equal(d.toString(), 'Time must match');
|
||||
created.nestedClass.should.have.property('roleName', 'admin');
|
||||
|
||||
const found = await Model.findById(created.id);
|
||||
should.exist(found);
|
||||
found.list.toJSON().should.deepEqual(['test']);
|
||||
found.arr.toJSON().should.deepEqual([1, 'str']);
|
||||
found.date.should.be.an.instanceOf(Date);
|
||||
found.date.toString().should.equal(d.toString(), 'Time must match');
|
||||
found.nestedClass.should.have.property('roleName', 'admin');
|
||||
});
|
||||
|
||||
it('should create nested objects defined by a class using multiple createAll calls', async () => {
|
||||
const d = new Date('2015-01-01T12:00:00');
|
||||
const result = await Promise.all([
|
||||
Model.createAll([
|
||||
{
|
||||
date: d,
|
||||
list: ['test 1'],
|
||||
arr: [1, 'str 1'],
|
||||
nestedClass: new NestedClass('admin 1'),
|
||||
},
|
||||
]),
|
||||
Model.createAll([
|
||||
{
|
||||
date: d,
|
||||
list: ['test 2'],
|
||||
arr: [2, 'str 2'],
|
||||
nestedClass: new NestedClass('admin 2'),
|
||||
},
|
||||
{
|
||||
date: d,
|
||||
list: ['test 3'],
|
||||
arr: [3, 'str 3'],
|
||||
nestedClass: new NestedClass('admin 3'),
|
||||
},
|
||||
]),
|
||||
Model.createAll([
|
||||
{
|
||||
date: d,
|
||||
list: ['test 4'],
|
||||
arr: [4, 'str 4'],
|
||||
nestedClass: new NestedClass('admin 4'),
|
||||
},
|
||||
]),
|
||||
Model.createAll([
|
||||
{
|
||||
date: d,
|
||||
list: ['test 6'],
|
||||
arr: [6, 'str 6'],
|
||||
nestedClass: new NestedClass('admin 6'),
|
||||
},
|
||||
]),
|
||||
Model.createAll([
|
||||
{
|
||||
date: d,
|
||||
list: ['test 5'],
|
||||
arr: [5, 'str 5'],
|
||||
nestedClass: new NestedClass('admin 5'),
|
||||
},
|
||||
]),
|
||||
]);
|
||||
const [created1] = result[0];
|
||||
const [created2, created3] = result[1];
|
||||
const [created4] = result[2];
|
||||
const [created6] = result[3];
|
||||
const [created5] = result[4];
|
||||
await created1.list.toJSON().should.deepEqual(['test 1']);
|
||||
created1.arr.toJSON().should.deepEqual([1, 'str 1']);
|
||||
created1.date.should.be.an.instanceOf(Date);
|
||||
created1.date.toString().should.equal(d.toString(), 'Time must match');
|
||||
created1.nestedClass.should.have.property('roleName', 'admin 1');
|
||||
await created2.list.toJSON().should.deepEqual(['test 2']);
|
||||
created2.arr.toJSON().should.deepEqual([2, 'str 2']);
|
||||
created2.date.should.be.an.instanceOf(Date);
|
||||
created2.date.toString().should.equal(d.toString(), 'Time must match');
|
||||
created2.nestedClass.should.have.property('roleName', 'admin 2');
|
||||
await created3.list.toJSON().should.deepEqual(['test 3']);
|
||||
created3.arr.toJSON().should.deepEqual([3, 'str 3']);
|
||||
created3.date.should.be.an.instanceOf(Date);
|
||||
created3.date.toString().should.equal(d.toString(), 'Time must match');
|
||||
created3.nestedClass.should.have.property('roleName', 'admin 3');
|
||||
await created4.list.toJSON().should.deepEqual(['test 4']);
|
||||
created4.arr.toJSON().should.deepEqual([4, 'str 4']);
|
||||
created4.date.should.be.an.instanceOf(Date);
|
||||
created4.date.toString().should.equal(d.toString(), 'Time must match');
|
||||
created4.nestedClass.should.have.property('roleName', 'admin 4');
|
||||
await created5.list.toJSON().should.deepEqual(['test 5']);
|
||||
created5.arr.toJSON().should.deepEqual([5, 'str 5']);
|
||||
created5.date.should.be.an.instanceOf(Date);
|
||||
created5.date.toString().should.equal(d.toString(), 'Time must match');
|
||||
created5.nestedClass.should.have.property('roleName', 'admin 5');
|
||||
await created6.list.toJSON().should.deepEqual(['test 6']);
|
||||
created6.arr.toJSON().should.deepEqual([6, 'str 6']);
|
||||
created6.date.should.be.an.instanceOf(Date);
|
||||
created6.date.toString().should.equal(d.toString(), 'Time must match');
|
||||
created6.nestedClass.should.have.property('roleName', 'admin 6');
|
||||
|
||||
const found1 = await Model.findById(created1.id);
|
||||
should.exist(found1);
|
||||
found1.list.toJSON().should.deepEqual(['test 1']);
|
||||
found1.arr.toJSON().should.deepEqual([1, 'str 1']);
|
||||
found1.date.should.be.an.instanceOf(Date);
|
||||
found1.date.toString().should.equal(d.toString(), 'Time must match');
|
||||
found1.nestedClass.should.have.property('roleName', 'admin 1');
|
||||
|
||||
const found2 = await Model.findById(created2.id);
|
||||
should.exist(found2);
|
||||
found2.list.toJSON().should.deepEqual(['test 2']);
|
||||
found2.arr.toJSON().should.deepEqual([2, 'str 2']);
|
||||
found2.date.should.be.an.instanceOf(Date);
|
||||
found2.date.toString().should.equal(d.toString(), 'Time must match');
|
||||
found2.nestedClass.should.have.property('roleName', 'admin 2');
|
||||
|
||||
const found3 = await Model.findById(created3.id);
|
||||
should.exist(found3);
|
||||
found3.list.toJSON().should.deepEqual(['test 3']);
|
||||
found3.arr.toJSON().should.deepEqual([3, 'str 3']);
|
||||
found3.date.should.be.an.instanceOf(Date);
|
||||
found3.date.toString().should.equal(d.toString(), 'Time must match');
|
||||
found3.nestedClass.should.have.property('roleName', 'admin 3');
|
||||
|
||||
const found4 = await Model.findById(created4.id);
|
||||
should.exist(found4);
|
||||
found4.list.toJSON().should.deepEqual(['test 4']);
|
||||
found4.arr.toJSON().should.deepEqual([4, 'str 4']);
|
||||
found4.date.should.be.an.instanceOf(Date);
|
||||
found4.date.toString().should.equal(d.toString(), 'Time must match');
|
||||
found4.nestedClass.should.have.property('roleName', 'admin 4');
|
||||
|
||||
const found5 = await Model.findById(created5.id);
|
||||
should.exist(found5);
|
||||
found5.list.toJSON().should.deepEqual(['test 5']);
|
||||
found5.arr.toJSON().should.deepEqual([5, 'str 5']);
|
||||
found5.date.should.be.an.instanceOf(Date);
|
||||
found5.date.toString().should.equal(d.toString(), 'Time must match');
|
||||
found5.nestedClass.should.have.property('roleName', 'admin 5');
|
||||
|
||||
const found6 = await Model.findById(created6.id);
|
||||
should.exist(found6);
|
||||
found6.list.toJSON().should.deepEqual(['test 6']);
|
||||
found6.arr.toJSON().should.deepEqual([6, 'str 6']);
|
||||
found6.date.should.be.an.instanceOf(Date);
|
||||
found6.date.toString().should.equal(d.toString(), 'Time must match');
|
||||
found6.nestedClass.should.have.property('roleName', 'admin 6');
|
||||
});
|
||||
|
||||
it('should respect data types when updating attributes', function(done) {
|
||||
var d = new Date, id;
|
||||
const d = new Date;
|
||||
let id;
|
||||
|
||||
Model.create({
|
||||
str: 'hello', date: d, num: '3', bool: 1}, function(err, m) {
|
||||
|
@ -151,24 +413,24 @@ describe('datatypes', function() {
|
|||
});
|
||||
|
||||
it('should not coerce nested objects into ModelConstructor types', function() {
|
||||
var coerced = Model._coerce({nested: {foo: 'bar'}});
|
||||
const coerced = Model._coerce({nested: {foo: 'bar'}});
|
||||
coerced.nested.constructor.name.should.equal('Object');
|
||||
});
|
||||
|
||||
it('rejects array value converted to NaN for a required property',
|
||||
function(done) {
|
||||
db = getSchema();
|
||||
Model = db.define('RequiredNumber', {
|
||||
num: {type: Number, required: true},
|
||||
});
|
||||
db.automigrate(['Model'], function() {
|
||||
Model.create({num: [1, 2, 3]}, function(err, inst) {
|
||||
should.exist(err);
|
||||
err.should.have.property('name').equal('ValidationError');
|
||||
done();
|
||||
function(done) {
|
||||
db = getSchema();
|
||||
Model = db.define('RequiredNumber', {
|
||||
num: {type: Number, required: true},
|
||||
});
|
||||
db.automigrate(['Model'], function() {
|
||||
Model.create({num: [1, 2, 3]}, function(err, inst) {
|
||||
should.exist(err);
|
||||
err.should.have.property('name').equal('ValidationError');
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('handles null data', (done) => {
|
||||
db = getSchema();
|
||||
|
@ -176,13 +438,13 @@ describe('datatypes', function() {
|
|||
data: {type: 'string'},
|
||||
});
|
||||
db.automigrate(['HandleNullModel'], function() {
|
||||
let a = new Model(null);
|
||||
const a = new Model(null);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
describe('model option persistUndefinedAsNull', function() {
|
||||
var TestModel, isStrict;
|
||||
let TestModel, isStrict;
|
||||
before(function(done) {
|
||||
db = getSchema();
|
||||
TestModel = db.define(
|
||||
|
@ -194,7 +456,8 @@ describe('datatypes', function() {
|
|||
},
|
||||
{
|
||||
persistUndefinedAsNull: true,
|
||||
});
|
||||
},
|
||||
);
|
||||
|
||||
isStrict = TestModel.definition.settings.strict;
|
||||
|
||||
|
@ -202,7 +465,7 @@ describe('datatypes', function() {
|
|||
});
|
||||
|
||||
it('should set missing optional properties to null', function(done) {
|
||||
var EXPECTED = {desc: null, stars: null};
|
||||
const EXPECTED = {desc: null, stars: null};
|
||||
TestModel.create({name: 'a-test-name'}, function(err, created) {
|
||||
if (err) return done(err);
|
||||
created.should.have.properties(EXPECTED);
|
||||
|
@ -216,8 +479,8 @@ describe('datatypes', function() {
|
|||
});
|
||||
|
||||
it('should convert property value undefined to null', function(done) {
|
||||
var EXPECTED = {desc: null, extra: null};
|
||||
var data = {desc: undefined, extra: undefined};
|
||||
const EXPECTED = {desc: null, extra: null};
|
||||
const data = {desc: undefined, extra: undefined};
|
||||
if (isStrict) {
|
||||
// SQL-based connectors don't support dynamic properties
|
||||
delete EXPECTED.extra;
|
||||
|
@ -237,21 +500,21 @@ describe('datatypes', function() {
|
|||
});
|
||||
|
||||
it('should convert undefined to null in the setter', function() {
|
||||
var inst = new TestModel();
|
||||
const inst = new TestModel();
|
||||
inst.desc = undefined;
|
||||
inst.should.have.property('desc', null);
|
||||
inst.toObject().should.have.property('desc', null);
|
||||
});
|
||||
|
||||
it('should use null in unsetAttribute()', function() {
|
||||
var inst = new TestModel();
|
||||
const inst = new TestModel();
|
||||
inst.unsetAttribute('stars');
|
||||
inst.should.have.property('stars', null);
|
||||
inst.toObject().should.have.property('stars', null);
|
||||
});
|
||||
|
||||
it('should convert undefined to null on save', function(done) {
|
||||
var EXPECTED = {desc: null, stars: null, extra: null, dx: null};
|
||||
const EXPECTED = {desc: null, stars: null, extra: null, dx: null};
|
||||
if (isStrict) {
|
||||
// SQL-based connectors don't support dynamic properties
|
||||
delete EXPECTED.extra;
|
||||
|
@ -283,13 +546,13 @@ describe('datatypes', function() {
|
|||
TestModel.modelName,
|
||||
{where: {id: created.id}},
|
||||
{},
|
||||
cb
|
||||
cb,
|
||||
);
|
||||
} else {
|
||||
TestModel.dataSource.connector.all(
|
||||
TestModel.modelName,
|
||||
{where: {id: created.id}},
|
||||
cb
|
||||
cb,
|
||||
);
|
||||
}
|
||||
});
|
||||
|
@ -297,7 +560,7 @@ describe('datatypes', function() {
|
|||
});
|
||||
|
||||
it('should convert undefined to null in toObject()', function() {
|
||||
var inst = new TestModel();
|
||||
const inst = new TestModel();
|
||||
inst.desc = undefined; // Note: this may be a no-op
|
||||
inst.unsetAttribute('stars');
|
||||
inst.extra = undefined;
|
||||
|
|
|
@ -1,25 +1,22 @@
|
|||
// Copyright IBM Corp. 2014,2016. All Rights Reserved.
|
||||
// Copyright IBM Corp. 2017,2019. All Rights Reserved.
|
||||
// Node module: loopback-datasource-juggler
|
||||
// This file is licensed under the MIT License.
|
||||
// License text available at https://opensource.org/licenses/MIT
|
||||
|
||||
/* global describe,it */
|
||||
/* jshint expr:true */
|
||||
|
||||
'use strict';
|
||||
|
||||
require('should');
|
||||
|
||||
var DateString = require('../lib/date-string');
|
||||
var fmt = require('util').format;
|
||||
var inspect = require('util').inspect;
|
||||
var os = require('os');
|
||||
const DateString = require('../lib/date-string');
|
||||
const fmt = require('util').format;
|
||||
const inspect = require('util').inspect;
|
||||
const os = require('os');
|
||||
|
||||
describe('DateString', function() {
|
||||
describe('constructor', function() {
|
||||
it('should support a valid date string', function() {
|
||||
var theDate = '2015-01-01';
|
||||
var date = new DateString(theDate);
|
||||
const theDate = '2015-01-01';
|
||||
const date = new DateString(theDate);
|
||||
date.should.not.eql(null);
|
||||
date.when.should.eql(theDate);
|
||||
date.toString().should.eql(theDate);
|
||||
|
@ -37,16 +34,23 @@ describe('DateString', function() {
|
|||
testInvalidInput('should throw on null input', null, 'Input must be a string');
|
||||
|
||||
it('should update internal date on set', function() {
|
||||
var date = new DateString('2015-01-01');
|
||||
const date = new DateString('2015-01-01');
|
||||
date.when = '2016-01-01';
|
||||
date.when.should.eql('2016-01-01');
|
||||
var d = new Date('2016-01-01');
|
||||
const d = new Date('2016-01-01');
|
||||
// The internal date representation should also be updated!
|
||||
date._date.toString().should.eql(d.toString());
|
||||
});
|
||||
|
||||
it('should accept DateString instance', function() {
|
||||
const input = new DateString('2015-01-01');
|
||||
const inst = new DateString(input);
|
||||
inst.toString().should.equal('2015-01-01');
|
||||
});
|
||||
|
||||
it('should return custom inspect output', function() {
|
||||
var date = new DateString('2015-01-01');
|
||||
var result = inspect(date);
|
||||
const date = new DateString('2015-01-01');
|
||||
const result = inspect(date);
|
||||
result.should.not.eql(null);
|
||||
result.should.eql(fmt('DateString ' + inspect({
|
||||
when: date.when,
|
||||
|
@ -55,24 +59,24 @@ describe('DateString', function() {
|
|||
});
|
||||
|
||||
it('should return JSON output', function() {
|
||||
var date = new DateString('2015-01-01');
|
||||
var result = date.toJSON();
|
||||
const date = new DateString('2015-01-01');
|
||||
const result = date.toJSON();
|
||||
result.should.eql(JSON.stringify({when: date.when}));
|
||||
});
|
||||
|
||||
function testValidInput(msg, val) {
|
||||
it(msg, function() {
|
||||
var theDate = new DateString(val);
|
||||
const theDate = new DateString(val);
|
||||
theDate.when.should.eql(val);
|
||||
var d = new Date(val);
|
||||
const d = new Date(val);
|
||||
theDate._date.toString().should.eql(d.toString());
|
||||
});
|
||||
}
|
||||
|
||||
function testInvalidInput(msg, val, err) {
|
||||
it(msg, () => {
|
||||
var fn = () => {
|
||||
var theDate = new DateString(val);
|
||||
const fn = () => {
|
||||
const theDate = new DateString(val);
|
||||
};
|
||||
fn.should.throw(err);
|
||||
});
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// Copyright IBM Corp. 2014,2016. All Rights Reserved.
|
||||
// Copyright IBM Corp. 2014,2019. All Rights Reserved.
|
||||
// Node module: loopback-datasource-juggler
|
||||
// This file is licensed under the MIT License.
|
||||
// License text available at https://opensource.org/licenses/MIT
|
||||
|
@ -7,10 +7,10 @@
|
|||
'use strict';
|
||||
|
||||
/* global getSchema:false */
|
||||
var should = require('./init.js');
|
||||
var async = require('async');
|
||||
const should = require('./init.js');
|
||||
const async = require('async');
|
||||
|
||||
var db, Category, Product, Tool, Widget, Thing, Person;
|
||||
let db, Category, Product, Tool, Widget, Thing, Person;
|
||||
|
||||
// This test requires a connector that can
|
||||
// handle a custom collection or table name
|
||||
|
@ -18,7 +18,7 @@ var db, Category, Product, Tool, Widget, Thing, Person;
|
|||
// TODO [fabien] add table for pgsql/mysql
|
||||
// TODO [fabien] change model definition - see #293
|
||||
|
||||
var setupProducts = function(ids, done) {
|
||||
const setupProducts = function(ids, done) {
|
||||
async.series([
|
||||
function(next) {
|
||||
Tool.create({name: 'Tool Z'}, function(err, inst) {
|
||||
|
@ -72,7 +72,7 @@ describe('default scope', function() {
|
|||
});
|
||||
|
||||
Product.lookupModel = function(data) {
|
||||
var m = this.dataSource.models[data.kind];
|
||||
const m = this.dataSource.models[data.kind];
|
||||
if (m.base === this) return m;
|
||||
return this;
|
||||
};
|
||||
|
@ -104,11 +104,11 @@ describe('default scope', function() {
|
|||
// inst is only valid for instance methods
|
||||
// like save, updateAttributes
|
||||
|
||||
var scopeFn = function(target, inst) {
|
||||
const scopeFn = function(target, inst) {
|
||||
return {where: {kind: this.modelName}};
|
||||
};
|
||||
|
||||
var propertiesFn = function(target, inst) {
|
||||
const propertiesFn = function(target, inst) {
|
||||
return {kind: this.modelName};
|
||||
};
|
||||
|
||||
|
@ -138,14 +138,14 @@ describe('default scope', function() {
|
|||
});
|
||||
|
||||
describe('manipulation', function() {
|
||||
var ids = {};
|
||||
const ids = {};
|
||||
|
||||
before(function(done) {
|
||||
db.automigrate(done);
|
||||
});
|
||||
|
||||
it('should return a scoped instance', function() {
|
||||
var p = new Tool({name: 'Product A', kind: 'ignored'});
|
||||
const p = new Tool({name: 'Product A', kind: 'ignored'});
|
||||
p.name.should.equal('Product A');
|
||||
p.kind.should.equal('Tool');
|
||||
p.setAttributes({kind: 'ignored'});
|
||||
|
@ -205,7 +205,7 @@ describe('default scope', function() {
|
|||
});
|
||||
|
||||
it('should update a scoped instance - updateOrCreate', function(done) {
|
||||
var data = {id: ids.productA, description: 'Anything...', kind: 'ingored'};
|
||||
const data = {id: ids.productA, description: 'Anything...', kind: 'ingored'};
|
||||
Tool.updateOrCreate(data, function(err, p) {
|
||||
should.not.exist(err);
|
||||
p.name.should.equal('Product A');
|
||||
|
@ -217,7 +217,7 @@ describe('default scope', function() {
|
|||
});
|
||||
|
||||
describe('findById', function() {
|
||||
var ids = {};
|
||||
const ids = {};
|
||||
|
||||
before(function(done) {
|
||||
db.automigrate(setupProducts.bind(null, ids, done));
|
||||
|
@ -250,7 +250,7 @@ describe('default scope', function() {
|
|||
});
|
||||
|
||||
describe('find', function() {
|
||||
var ids = {};
|
||||
const ids = {};
|
||||
|
||||
before(function(done) {
|
||||
db.automigrate(setupProducts.bind(null, ids, done));
|
||||
|
@ -322,7 +322,7 @@ describe('default scope', function() {
|
|||
});
|
||||
|
||||
describe('exists', function() {
|
||||
var ids = {};
|
||||
const ids = {};
|
||||
|
||||
before(function(done) {
|
||||
db.automigrate(setupProducts.bind(null, ids, done));
|
||||
|
@ -370,7 +370,7 @@ describe('default scope', function() {
|
|||
});
|
||||
|
||||
describe('count', function() {
|
||||
var ids = {};
|
||||
const ids = {};
|
||||
|
||||
before(function(done) {
|
||||
db.automigrate(setupProducts.bind(null, ids, done));
|
||||
|
@ -418,7 +418,7 @@ describe('default scope', function() {
|
|||
});
|
||||
|
||||
describe('removeById', function() {
|
||||
var ids = {};
|
||||
const ids = {};
|
||||
|
||||
function isDeleted(id, done) {
|
||||
Product.exists(id, function(err, exists) {
|
||||
|
@ -426,7 +426,7 @@ describe('default scope', function() {
|
|||
exists.should.be.false;
|
||||
done();
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
before(function(done) {
|
||||
db.automigrate(setupProducts.bind(null, ids, done));
|
||||
|
@ -476,7 +476,7 @@ describe('default scope', function() {
|
|||
});
|
||||
|
||||
describe('update', function() {
|
||||
var ids = {};
|
||||
const ids = {};
|
||||
|
||||
before(function(done) {
|
||||
db.automigrate(setupProducts.bind(null, ids, done));
|
||||
|
@ -521,7 +521,7 @@ describe('default scope', function() {
|
|||
});
|
||||
|
||||
describe('remove', function() {
|
||||
var ids = {};
|
||||
const ids = {};
|
||||
|
||||
before(function(done) {
|
||||
db.automigrate(setupProducts.bind(null, ids, done));
|
||||
|
@ -593,7 +593,7 @@ describe('default scope', function() {
|
|||
});
|
||||
|
||||
describe('scopes', function() {
|
||||
var ids = {};
|
||||
const ids = {};
|
||||
|
||||
before(function(done) {
|
||||
db.automigrate(setupProducts.bind(null, ids, done));
|
||||
|
@ -667,12 +667,13 @@ describe('default scope', function() {
|
|||
});
|
||||
});
|
||||
|
||||
// eslint-disable-next-line mocha/no-identical-title
|
||||
it('should find a scoped instance - thing', function(done) {
|
||||
Product.find({where: {name: 'Product'}}, function(err, products) {
|
||||
products.should.have.length(2);
|
||||
products[0].name.should.equal('Product');
|
||||
products[1].name.should.equal('Product');
|
||||
var kinds = products.map(function(p) { return p.kind; });
|
||||
const kinds = products.map(function(p) { return p.kind; });
|
||||
kinds.sort();
|
||||
kinds.should.eql(['Thing', 'Widget']);
|
||||
done();
|
||||
|
@ -681,7 +682,7 @@ describe('default scope', function() {
|
|||
});
|
||||
|
||||
describe('relations', function() {
|
||||
var ids = {};
|
||||
const ids = {};
|
||||
|
||||
before(function(done) {
|
||||
db.automigrate(done);
|
||||
|
@ -816,7 +817,7 @@ describe('default scope', function() {
|
|||
Person.findById(1, function(err, person) {
|
||||
should.not.exist(err);
|
||||
should.exist(person);
|
||||
var things = person.things();
|
||||
const things = person.things();
|
||||
should.exist(things);
|
||||
things.should.be.an.instanceOf(Array);
|
||||
things.should.have.length(1);
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// Copyright IBM Corp. 2013,2016. All Rights Reserved.
|
||||
// Copyright IBM Corp. 2013,2019. All Rights Reserved.
|
||||
// Node module: loopback-datasource-juggler
|
||||
// This file is licensed under the MIT License.
|
||||
// License text available at https://opensource.org/licenses/MIT
|
||||
|
@ -7,12 +7,12 @@
|
|||
'use strict';
|
||||
|
||||
/* global getSchema:false */
|
||||
var should = require('./init.js');
|
||||
const should = require('./init.js');
|
||||
|
||||
var db = getSchema();
|
||||
const db = getSchema();
|
||||
|
||||
describe('defaults', function() {
|
||||
var Server;
|
||||
let Server;
|
||||
|
||||
before(function() {
|
||||
Server = db.define('Server', {
|
||||
|
@ -23,7 +23,7 @@ describe('defaults', function() {
|
|||
});
|
||||
|
||||
it('should apply defaults on new', function() {
|
||||
var s = new Server;
|
||||
const s = new Server;
|
||||
s.port.should.equal(80);
|
||||
});
|
||||
|
||||
|
@ -34,13 +34,13 @@ describe('defaults', function() {
|
|||
});
|
||||
});
|
||||
|
||||
it('should apply defaults on read', function(done) {
|
||||
it('should NOT apply defaults on read', function(done) {
|
||||
db.defineProperty('Server', 'host', {
|
||||
type: String,
|
||||
default: 'localhost',
|
||||
});
|
||||
Server.all(function(err, servers) {
|
||||
(new String('localhost')).should.equal(servers[0].host);
|
||||
should(servers[0].host).be.undefined();
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
@ -49,10 +49,9 @@ describe('defaults', function() {
|
|||
Server.create({host: 'localhost', port: 8080}, function(err, s) {
|
||||
should.not.exist(err);
|
||||
s.port.should.equal(8080);
|
||||
Server.find({fields: ['host']}, function(err, servers) {
|
||||
servers[0].host.should.equal('localhost');
|
||||
servers[0].should.have.property('host');
|
||||
servers[0].should.have.property('port', undefined);
|
||||
Server.findById(s.id, {fields: ['host']}, function(err, server) {
|
||||
server.should.have.property('host', 'localhost');
|
||||
server.should.have.property('port', undefined);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
@ -76,4 +75,103 @@ describe('defaults', function() {
|
|||
});
|
||||
});
|
||||
});
|
||||
|
||||
context('applyDefaultOnWrites', function() {
|
||||
it('does not affect default behavior when not set', async () => {
|
||||
const Apple = db.define('Apple', {
|
||||
color: {type: String, default: 'red'},
|
||||
taste: {type: String, default: 'sweet'},
|
||||
}, {applyDefaultsOnReads: false});
|
||||
|
||||
const apple = await Apple.create();
|
||||
apple.color.should.equal('red');
|
||||
apple.taste.should.equal('sweet');
|
||||
});
|
||||
|
||||
it('removes the property when set to `false`', async () => {
|
||||
const Apple = db.define('Apple', {
|
||||
color: {type: String, default: 'red', applyDefaultOnWrites: false},
|
||||
taste: {type: String, default: 'sweet'},
|
||||
}, {applyDefaultsOnReads: false});
|
||||
|
||||
const apple = await Apple.create({color: 'red', taste: 'sweet'});
|
||||
const found = await Apple.findById(apple.id);
|
||||
should(found.color).be.undefined();
|
||||
found.taste.should.equal('sweet');
|
||||
});
|
||||
|
||||
it('removes nested property in an object when set to `false`', async () => {
|
||||
const Apple = db.define('Apple', {
|
||||
name: {type: String},
|
||||
qualities: {
|
||||
color: {type: String, default: 'red', applyDefaultOnWrites: false},
|
||||
taste: {type: String, default: 'sweet'},
|
||||
},
|
||||
}, {applyDefaultsOnReads: false});
|
||||
|
||||
const apple = await Apple.create({name: 'Honeycrisp', qualities: {taste: 'sweet'}});
|
||||
const found = await Apple.findById(apple.id);
|
||||
should(found.qualities.color).be.undefined();
|
||||
found.qualities.taste.should.equal('sweet');
|
||||
});
|
||||
|
||||
it('removes nested property in an array when set to `false', async () => {
|
||||
const Apple = db.define('Apple', {
|
||||
name: {type: String},
|
||||
qualities: [
|
||||
{color: {type: String, default: 'red', applyDefaultOnWrites: false}},
|
||||
{taste: {type: String, default: 'sweet'}},
|
||||
],
|
||||
}, {applyDefaultsOnReads: false});
|
||||
|
||||
const apple = await Apple.create({name: 'Honeycrisp', qualities: [{taste: 'sweet'}]});
|
||||
const found = await Apple.findById(apple.id);
|
||||
should(found.qualities[0].color).be.undefined();
|
||||
found.qualities.length.should.equal(1);
|
||||
});
|
||||
});
|
||||
|
||||
context('persistDefaultValues', function() {
|
||||
it('removes property if value matches default', async () => {
|
||||
const Apple = db.define('Apple', {
|
||||
color: {type: String, default: 'red', persistDefaultValues: false},
|
||||
taste: {type: String, default: 'sweet'},
|
||||
}, {applyDefaultsOnReads: false});
|
||||
|
||||
const apple = await Apple.create({color: 'red', taste: 'sweet'});
|
||||
const found = await Apple.findById(apple.id);
|
||||
should(found.color).be.undefined();
|
||||
found.taste.should.equal('sweet');
|
||||
});
|
||||
|
||||
it('removes property if value matches default in an object', async () => {
|
||||
const Apple = db.define('Apple', {
|
||||
name: {type: String},
|
||||
qualities: {
|
||||
color: {type: String, default: 'red', persistDefaultValues: false},
|
||||
taste: {type: String, default: 'sweet'},
|
||||
},
|
||||
}, {applyDefaultsOnReads: false});
|
||||
|
||||
const apple = await Apple.create({name: 'Honeycrisp', qualities: {taste: 'sweet'}});
|
||||
const found = await Apple.findById(apple.id);
|
||||
should(found.qualities.color).be.undefined();
|
||||
found.qualities.taste.should.equal('sweet');
|
||||
});
|
||||
|
||||
it('removes property if value matches default in an array', async () => {
|
||||
const Apple = db.define('Apple', {
|
||||
name: {type: String},
|
||||
qualities: [
|
||||
{color: {type: String, default: 'red', persistDefaultValues: false}},
|
||||
{taste: {type: String, default: 'sweet'}},
|
||||
],
|
||||
}, {applyDefaultsOnReads: false});
|
||||
|
||||
const apple = await Apple.create({name: 'Honeycrisp', qualities: [{taste: 'sweet'}]});
|
||||
const found = await Apple.findById(apple.id);
|
||||
should(found.qualities[0].color).be.undefined();
|
||||
found.qualities.length.should.equal(1);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -1,21 +1,21 @@
|
|||
// Copyright IBM Corp. 2015,2016. All Rights Reserved.
|
||||
// Copyright IBM Corp. 2015,2019. All Rights Reserved.
|
||||
// Node module: loopback-datasource-juggler
|
||||
// This file is licensed under the MIT License.
|
||||
// License text available at https://opensource.org/licenses/MIT
|
||||
|
||||
'use strict';
|
||||
|
||||
var jdb = require('../');
|
||||
var DataSource = jdb.DataSource;
|
||||
var should = require('./init.js');
|
||||
const jdb = require('../');
|
||||
const DataSource = jdb.DataSource;
|
||||
const should = require('./init.js');
|
||||
|
||||
describe('Memory connector with mocked discovery', function() {
|
||||
var ds;
|
||||
let ds;
|
||||
|
||||
before(function() {
|
||||
ds = new DataSource({connector: 'memory'});
|
||||
|
||||
var models = [{type: 'table', name: 'CUSTOMER', owner: 'STRONGLOOP'},
|
||||
const models = [{type: 'table', name: 'CUSTOMER', owner: 'STRONGLOOP'},
|
||||
{type: 'table', name: 'INVENTORY', owner: 'STRONGLOOP'},
|
||||
{type: 'table', name: 'LOCATION', owner: 'STRONGLOOP'}];
|
||||
|
||||
|
@ -25,7 +25,7 @@ describe('Memory connector with mocked discovery', function() {
|
|||
});
|
||||
};
|
||||
|
||||
var modelProperties = [{
|
||||
const modelProperties = [{
|
||||
owner: 'STRONGLOOP',
|
||||
tableName: 'INVENTORY',
|
||||
columnName: 'PRODUCT_ID',
|
||||
|
@ -34,6 +34,7 @@ describe('Memory connector with mocked discovery', function() {
|
|||
dataPrecision: null,
|
||||
dataScale: null,
|
||||
nullable: 0,
|
||||
generated: true,
|
||||
},
|
||||
{
|
||||
owner: 'STRONGLOOP',
|
||||
|
@ -44,6 +45,7 @@ describe('Memory connector with mocked discovery', function() {
|
|||
dataPrecision: null,
|
||||
dataScale: null,
|
||||
nullable: 0,
|
||||
generated: false,
|
||||
},
|
||||
{
|
||||
owner: 'STRONGLOOP',
|
||||
|
@ -54,6 +56,7 @@ describe('Memory connector with mocked discovery', function() {
|
|||
dataPrecision: 10,
|
||||
dataScale: 0,
|
||||
nullable: 1,
|
||||
generated: false,
|
||||
},
|
||||
{
|
||||
owner: 'STRONGLOOP',
|
||||
|
@ -64,6 +67,7 @@ describe('Memory connector with mocked discovery', function() {
|
|||
dataPrecision: 10,
|
||||
dataScale: 0,
|
||||
nullable: 1,
|
||||
generated: false,
|
||||
}];
|
||||
|
||||
ds.discoverModelProperties = function(modelName, options, cb) {
|
||||
|
@ -73,14 +77,41 @@ describe('Memory connector with mocked discovery', function() {
|
|||
};
|
||||
});
|
||||
|
||||
it('should convert table/column names to camel cases', function(done) {
|
||||
it('should convert table names to pascal cases and column names to camel case', function(done) {
|
||||
ds.discoverSchemas('INVENTORY', {}, function(err, schemas) {
|
||||
if (err) return done(err);
|
||||
schemas.should.have.property('STRONGLOOP.INVENTORY');
|
||||
var s = schemas['STRONGLOOP.INVENTORY'];
|
||||
const s = schemas['STRONGLOOP.INVENTORY'];
|
||||
s.name.should.be.eql('Inventory');
|
||||
Object.keys(s.properties).should.be.eql(
|
||||
['productId', 'locationId', 'available', 'total']);
|
||||
['productId', 'locationId', 'available', 'total'],
|
||||
);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should have jsonSchema: {nullable: true} in property for `available`', function(done) {
|
||||
ds.discoverSchemas('INVENTORY', {}, function(err, schemas) {
|
||||
if (err) return done(err);
|
||||
schemas.should.have.property('STRONGLOOP.INVENTORY');
|
||||
const s = schemas['STRONGLOOP.INVENTORY'];
|
||||
s.name.should.be.eql('Inventory');
|
||||
s.properties.available.should.have.property('jsonSchema');
|
||||
s.properties.available.jsonSchema.should.have.property('nullable');
|
||||
s.properties.available.jsonSchema.nullable.should.be.eql(true);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should keep the column names the same as database', function(done) {
|
||||
ds.discoverSchemas('INVENTORY', {disableCamelCase: true}, function(err, schemas) {
|
||||
if (err) return done(err);
|
||||
schemas.should.have.property('STRONGLOOP.INVENTORY');
|
||||
const s = schemas['STRONGLOOP.INVENTORY'];
|
||||
s.name.should.be.eql('Inventory');
|
||||
Object.keys(s.properties).should.be.eql(
|
||||
['PRODUCT_ID', 'LOCATION_ID', 'AVAILABLE', 'TOTAL'],
|
||||
);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
@ -94,10 +125,11 @@ describe('Memory connector with mocked discovery', function() {
|
|||
}, function(err, schemas) {
|
||||
if (err) return done(err);
|
||||
schemas.should.have.property('STRONGLOOP.INVENTORY');
|
||||
var s = schemas['STRONGLOOP.INVENTORY'];
|
||||
const s = schemas['STRONGLOOP.INVENTORY'];
|
||||
s.name.should.be.eql('inventory');
|
||||
Object.keys(s.properties).should.be.eql(
|
||||
['product_id', 'location_id', 'available', 'total']);
|
||||
['product_id', 'location_id', 'available', 'total'],
|
||||
);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
@ -107,17 +139,18 @@ describe('Memory connector with mocked discovery', function() {
|
|||
ds.discoverSchemas('INVENTORY', {nameMapper: null}, function(err, schemas) {
|
||||
if (err) return done(err);
|
||||
schemas.should.have.property('STRONGLOOP.INVENTORY');
|
||||
var s = schemas['STRONGLOOP.INVENTORY'];
|
||||
const s = schemas['STRONGLOOP.INVENTORY'];
|
||||
s.name.should.be.eql('INVENTORY');
|
||||
Object.keys(s.properties).should.be.eql(
|
||||
['PRODUCT_ID', 'LOCATION_ID', 'AVAILABLE', 'TOTAL']);
|
||||
['PRODUCT_ID', 'LOCATION_ID', 'AVAILABLE', 'TOTAL'],
|
||||
);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should honor connector\'s discoverSchemas implementation',
|
||||
function(done) {
|
||||
var models = {
|
||||
const models = {
|
||||
inventory: {
|
||||
product: {type: 'string'},
|
||||
location: {type: 'string'},
|
||||
|
@ -137,7 +170,7 @@ describe('Memory connector with mocked discovery', function() {
|
|||
|
||||
it('should callback function, passed as options parameter',
|
||||
function(done) {
|
||||
var models = {
|
||||
const models = {
|
||||
inventory: {
|
||||
product: {type: 'string'},
|
||||
location: {type: 'string'},
|
||||
|
@ -149,7 +182,7 @@ describe('Memory connector with mocked discovery', function() {
|
|||
});
|
||||
};
|
||||
|
||||
var options = function(err, schemas) {
|
||||
const options = function(err, schemas) {
|
||||
if (err) return done(err);
|
||||
schemas.should.be.eql(models);
|
||||
done();
|
||||
|
@ -162,25 +195,25 @@ describe('Memory connector with mocked discovery', function() {
|
|||
function(done) {
|
||||
ds.connector.discoverSchemas = null;
|
||||
ds.discoverSchemas('INVENTORY', {})
|
||||
.then(function(schemas) {
|
||||
schemas.should.have.property('STRONGLOOP.INVENTORY');
|
||||
.then(function(schemas) {
|
||||
schemas.should.have.property('STRONGLOOP.INVENTORY');
|
||||
|
||||
var s = schemas['STRONGLOOP.INVENTORY'];
|
||||
s.name.should.be.eql('Inventory');
|
||||
const s = schemas['STRONGLOOP.INVENTORY'];
|
||||
s.name.should.be.eql('Inventory');
|
||||
|
||||
Object.keys(s.properties).should.be.eql(
|
||||
['productId', 'locationId', 'available', 'total']
|
||||
);
|
||||
done();
|
||||
})
|
||||
.catch(function(err) {
|
||||
done(err);
|
||||
});
|
||||
Object.keys(s.properties).should.be.eql(
|
||||
['productId', 'locationId', 'available', 'total'],
|
||||
);
|
||||
done();
|
||||
})
|
||||
.catch(function(err) {
|
||||
done(err);
|
||||
});
|
||||
});
|
||||
|
||||
describe('discoverSchema', function() {
|
||||
var models;
|
||||
var schema;
|
||||
let models;
|
||||
let schema;
|
||||
before(function() {
|
||||
schema = {
|
||||
name: 'Inventory',
|
||||
|
@ -191,6 +224,9 @@ describe('Memory connector with mocked discovery', function() {
|
|||
properties: {
|
||||
available: {
|
||||
length: null,
|
||||
jsonSchema: {
|
||||
nullable: true,
|
||||
},
|
||||
memory: {
|
||||
columnName: 'AVAILABLE',
|
||||
dataLength: null,
|
||||
|
@ -198,14 +234,19 @@ describe('Memory connector with mocked discovery', function() {
|
|||
dataScale: 0,
|
||||
dataType: 'int',
|
||||
nullable: 1,
|
||||
generated: false,
|
||||
},
|
||||
precision: 10,
|
||||
required: false,
|
||||
scale: 0,
|
||||
type: undefined,
|
||||
generated: false,
|
||||
},
|
||||
locationId: {
|
||||
length: 20,
|
||||
jsonSchema: {
|
||||
nullable: false,
|
||||
},
|
||||
memory: {
|
||||
columnName: 'LOCATION_ID',
|
||||
dataLength: 20,
|
||||
|
@ -213,14 +254,19 @@ describe('Memory connector with mocked discovery', function() {
|
|||
dataScale: null,
|
||||
dataType: 'varchar',
|
||||
nullable: 0,
|
||||
generated: false,
|
||||
},
|
||||
precision: null,
|
||||
required: true,
|
||||
scale: null,
|
||||
type: undefined,
|
||||
generated: false,
|
||||
},
|
||||
productId: {
|
||||
length: 20,
|
||||
jsonSchema: {
|
||||
nullable: false,
|
||||
},
|
||||
memory: {
|
||||
columnName: 'PRODUCT_ID',
|
||||
dataLength: 20,
|
||||
|
@ -228,14 +274,19 @@ describe('Memory connector with mocked discovery', function() {
|
|||
dataScale: null,
|
||||
dataType: 'varchar',
|
||||
nullable: 0,
|
||||
generated: true,
|
||||
},
|
||||
precision: null,
|
||||
required: true,
|
||||
required: false,
|
||||
scale: null,
|
||||
type: undefined,
|
||||
generated: true,
|
||||
},
|
||||
total: {
|
||||
length: null,
|
||||
jsonSchema: {
|
||||
nullable: true,
|
||||
},
|
||||
memory: {
|
||||
columnName: 'TOTAL',
|
||||
dataLength: null,
|
||||
|
@ -243,11 +294,13 @@ describe('Memory connector with mocked discovery', function() {
|
|||
dataScale: 0,
|
||||
dataType: 'int',
|
||||
nullable: 1,
|
||||
generated: false,
|
||||
},
|
||||
precision: 10,
|
||||
required: false,
|
||||
scale: 0,
|
||||
type: undefined,
|
||||
generated: false,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
@ -262,7 +315,7 @@ describe('Memory connector with mocked discovery', function() {
|
|||
});
|
||||
|
||||
it('should callback function, passed as options parameter', function(done) {
|
||||
var options = function(err, schemas) {
|
||||
const options = function(err, schemas) {
|
||||
if (err) return done(err);
|
||||
schemas.should.be.eql(schema);
|
||||
done();
|
||||
|
@ -285,11 +338,11 @@ describe('Memory connector with mocked discovery', function() {
|
|||
});
|
||||
|
||||
describe('discoverModelDefinitions', function() {
|
||||
var ds;
|
||||
let ds;
|
||||
before(function() {
|
||||
ds = new DataSource({connector: 'memory'});
|
||||
|
||||
var models = [{type: 'table', name: 'CUSTOMER', owner: 'STRONGLOOP'},
|
||||
const models = [{type: 'table', name: 'CUSTOMER', owner: 'STRONGLOOP'},
|
||||
{type: 'table', name: 'INVENTORY', owner: 'STRONGLOOP'},
|
||||
{type: 'table', name: 'LOCATION', owner: 'STRONGLOOP'}];
|
||||
|
||||
|
@ -304,27 +357,27 @@ describe('discoverModelDefinitions', function() {
|
|||
ds.discoverModelDefinitions({}, function(err, schemas) {
|
||||
if (err) return done(err);
|
||||
|
||||
var tableNames = schemas.map(function(s) {
|
||||
const tableNames = schemas.map(function(s) {
|
||||
return s.name;
|
||||
});
|
||||
|
||||
tableNames.should.be.eql(
|
||||
['CUSTOMER', 'INVENTORY', 'LOCATION']
|
||||
['CUSTOMER', 'INVENTORY', 'LOCATION'],
|
||||
);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should callback function, passed as options parameter', function(done) {
|
||||
var options = function(err, schemas) {
|
||||
const options = function(err, schemas) {
|
||||
if (err) return done(err);
|
||||
|
||||
var tableNames = schemas.map(function(s) {
|
||||
const tableNames = schemas.map(function(s) {
|
||||
return s.name;
|
||||
});
|
||||
|
||||
tableNames.should.be.eql(
|
||||
['CUSTOMER', 'INVENTORY', 'LOCATION']
|
||||
['CUSTOMER', 'INVENTORY', 'LOCATION'],
|
||||
);
|
||||
done();
|
||||
};
|
||||
|
@ -335,24 +388,24 @@ describe('discoverModelDefinitions', function() {
|
|||
it('should discover model using `discoverModelDefinitions` - promise variant', function(done) {
|
||||
ds.discoverModelDefinitions({})
|
||||
.then(function(schemas) {
|
||||
var tableNames = schemas.map(function(s) {
|
||||
const tableNames = schemas.map(function(s) {
|
||||
return s.name;
|
||||
});
|
||||
|
||||
tableNames.should.be.eql(
|
||||
['CUSTOMER', 'INVENTORY', 'LOCATION']
|
||||
['CUSTOMER', 'INVENTORY', 'LOCATION'],
|
||||
);
|
||||
done();
|
||||
})
|
||||
.catch(function(err) {
|
||||
done(err);
|
||||
});
|
||||
.catch(function(err) {
|
||||
done(err);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('discoverModelProperties', function() {
|
||||
var ds;
|
||||
var modelProperties;
|
||||
let ds;
|
||||
let modelProperties;
|
||||
before(function() {
|
||||
ds = new DataSource({connector: 'memory'});
|
||||
|
||||
|
@ -365,6 +418,7 @@ describe('discoverModelProperties', function() {
|
|||
dataPrecision: null,
|
||||
dataScale: null,
|
||||
nullable: 0,
|
||||
generated: false,
|
||||
},
|
||||
{
|
||||
owner: 'STRONGLOOP',
|
||||
|
@ -375,6 +429,7 @@ describe('discoverModelProperties', function() {
|
|||
dataPrecision: null,
|
||||
dataScale: null,
|
||||
nullable: 0,
|
||||
generated: false,
|
||||
},
|
||||
{
|
||||
owner: 'STRONGLOOP',
|
||||
|
@ -405,7 +460,7 @@ describe('discoverModelProperties', function() {
|
|||
});
|
||||
|
||||
it('should callback function, passed as options parameter', function(done) {
|
||||
var options = function(err, schemas) {
|
||||
const options = function(err, schemas) {
|
||||
if (err) return done(err);
|
||||
|
||||
schemas.should.be.eql(modelProperties);
|
||||
|
@ -430,15 +485,15 @@ describe('discoverModelProperties', function() {
|
|||
schemas.should.be.eql(modelProperties);
|
||||
done();
|
||||
})
|
||||
.catch(function(err) {
|
||||
done(err);
|
||||
});
|
||||
.catch(function(err) {
|
||||
done(err);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('discoverPrimaryKeys', function() {
|
||||
var ds;
|
||||
var modelProperties, primaryKeys;
|
||||
let ds;
|
||||
let modelProperties, primaryKeys;
|
||||
before(function() {
|
||||
ds = new DataSource({connector: 'memory'});
|
||||
|
||||
|
@ -475,7 +530,7 @@ describe('discoverPrimaryKeys', function() {
|
|||
});
|
||||
|
||||
it('should callback function, passed as options parameter', function(done) {
|
||||
var options = function(err, modelPrimaryKeys) {
|
||||
const options = function(err, modelPrimaryKeys) {
|
||||
if (err) return done(err);
|
||||
|
||||
modelPrimaryKeys.should.be.eql(primaryKeys);
|
||||
|
@ -497,8 +552,8 @@ describe('discoverPrimaryKeys', function() {
|
|||
});
|
||||
|
||||
describe('discoverForeignKeys', function() {
|
||||
var ds;
|
||||
var modelProperties, foreignKeys;
|
||||
let ds;
|
||||
let modelProperties, foreignKeys;
|
||||
before(function() {
|
||||
ds = new DataSource({connector: 'memory'});
|
||||
|
||||
|
@ -531,7 +586,7 @@ describe('discoverForeignKeys', function() {
|
|||
});
|
||||
|
||||
it('should callback function, passed as options parameter', function(done) {
|
||||
var options = function(err, modelForeignKeys) {
|
||||
const options = function(err, modelForeignKeys) {
|
||||
if (err) return done(err);
|
||||
|
||||
modelForeignKeys.should.be.eql(foreignKeys);
|
||||
|
@ -554,8 +609,8 @@ describe('discoverForeignKeys', function() {
|
|||
});
|
||||
|
||||
describe('discoverExportedForeignKeys', function() {
|
||||
var ds;
|
||||
var modelProperties, exportedForeignKeys;
|
||||
let ds;
|
||||
let modelProperties, exportedForeignKeys;
|
||||
before(function() {
|
||||
ds = new DataSource({connector: 'memory'});
|
||||
|
||||
|
@ -588,7 +643,7 @@ describe('discoverExportedForeignKeys', function() {
|
|||
});
|
||||
|
||||
it('should callback function, passed as options parameter', function(done) {
|
||||
var options = function(err, modelForeignKeys) {
|
||||
const options = function(err, modelForeignKeys) {
|
||||
if (err) return done(err);
|
||||
|
||||
modelForeignKeys.should.be.eql(exportedForeignKeys);
|
||||
|
@ -599,22 +654,22 @@ describe('discoverExportedForeignKeys', function() {
|
|||
});
|
||||
|
||||
it('should discover foreign key definitions using `discoverExportedForeignKeys` - promise variant',
|
||||
function(done) {
|
||||
ds.discoverExportedForeignKeys('INVENTORY', {})
|
||||
.then(function(modelForeignKeys) {
|
||||
modelForeignKeys.should.be.eql(exportedForeignKeys);
|
||||
done();
|
||||
})
|
||||
.catch(function(err) {
|
||||
done(err);
|
||||
});
|
||||
});
|
||||
function(done) {
|
||||
ds.discoverExportedForeignKeys('INVENTORY', {})
|
||||
.then(function(modelForeignKeys) {
|
||||
modelForeignKeys.should.be.eql(exportedForeignKeys);
|
||||
done();
|
||||
})
|
||||
.catch(function(err) {
|
||||
done(err);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('Mock connector', function() {
|
||||
var mockConnectors = require('./mock-connectors');
|
||||
const mockConnectors = require('./mock-connectors');
|
||||
describe('customFieldSettings', function() {
|
||||
var ds = new DataSource(mockConnectors.customFieldSettings);
|
||||
const ds = new DataSource(mockConnectors.customFieldSettings);
|
||||
|
||||
it('should be present in discoverSchemas', function(done) {
|
||||
ds.discoverSchemas('person', function(err, schemas) {
|
||||
|
@ -629,7 +684,8 @@ describe('Mock connector', function() {
|
|||
});
|
||||
|
||||
describe('Default memory connector', function() {
|
||||
var ds, nonExistantError = 'Table \'NONEXISTENT\' does not exist.';
|
||||
const nonExistantError = 'Table \'NONEXISTENT\' does not exist.';
|
||||
let ds;
|
||||
|
||||
before(function() {
|
||||
ds = new DataSource({connector: 'memory'});
|
||||
|
|
|
@ -1,32 +1,32 @@
|
|||
// Copyright IBM Corp. 2013,2016. All Rights Reserved.
|
||||
// Copyright IBM Corp. 2017,2019. All Rights Reserved.
|
||||
// Node module: loopback-datasource-juggler
|
||||
// This file is licensed under the MIT License.
|
||||
// License text available at https://opensource.org/licenses/MIT
|
||||
|
||||
// This test written in mocha+should.js
|
||||
'use strict';
|
||||
var should = require('./init.js');
|
||||
var assert = require('assert');
|
||||
const should = require('./init.js');
|
||||
const assert = require('assert');
|
||||
|
||||
var jdb = require('../');
|
||||
var ModelBuilder = jdb.ModelBuilder;
|
||||
const jdb = require('../');
|
||||
const ModelBuilder = jdb.ModelBuilder;
|
||||
|
||||
describe('exclude properties ', function() {
|
||||
it('from base model', function(done) {
|
||||
var ds = new ModelBuilder();
|
||||
const ds = new ModelBuilder();
|
||||
// create a base model User which has name and password properties. id property gets
|
||||
// internally created for the User Model
|
||||
var User = ds.define('User', {name: String, password: String});
|
||||
var properties = User.definition.properties;
|
||||
const User = ds.define('User', {name: String, password: String});
|
||||
let properties = User.definition.properties;
|
||||
// User should have id, name & password properties
|
||||
assert(('id' in properties) && ('password' in properties) && ('name' in properties),
|
||||
assert(('id' in properties) && ('password' in properties) && ('name' in properties),
|
||||
'User should have id, name & password properties');
|
||||
// Create sub model Customer with vip as property. id property gets automatically created here as well.
|
||||
// Customer will inherit name, password and id from base User model.
|
||||
// With excludeBaseProperties, 'password' and 'id' gets excluded from base User model
|
||||
// With idInjection: false - id property of sub Model Customer gets excluded. At the end
|
||||
// User will have these 2 properties: name (inherited from User model) and vip (from customer Model).
|
||||
var Customer = User.extend('Customer', {vip: {type: String}},
|
||||
const Customer = User.extend('Customer', {vip: {type: String}},
|
||||
{idInjection: false, excludeBaseProperties: ['password', 'id']});
|
||||
// Customer should have these properties: name(from UserModel) & vip
|
||||
properties = Customer.definition.properties;
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// Copyright IBM Corp. 2013,2016. All Rights Reserved.
|
||||
// Copyright IBM Corp. 2017,2019. All Rights Reserved.
|
||||
// Node module: loopback-datasource-juggler
|
||||
// This file is licensed under the MIT License.
|
||||
// License text available at https://opensource.org/licenses/MIT
|
||||
|
|
|
@ -1,45 +1,42 @@
|
|||
// Copyright IBM Corp. 2014,2016. All Rights Reserved.
|
||||
// Copyright IBM Corp. 2014,2019. All Rights Reserved.
|
||||
// Node module: loopback-datasource-juggler
|
||||
// This file is licensed under the MIT License.
|
||||
// License text available at https://opensource.org/licenses/MIT
|
||||
|
||||
/* global describe,it */
|
||||
/* jshint expr:true */
|
||||
|
||||
'use strict';
|
||||
|
||||
require('should');
|
||||
|
||||
var GeoPoint = require('../lib/geo').GeoPoint;
|
||||
var nearFilter = require('../lib/geo').nearFilter;
|
||||
var geoFilter = require('../lib/geo').filter;
|
||||
var DELTA = 0.0000001;
|
||||
const GeoPoint = require('../lib/geo').GeoPoint;
|
||||
const nearFilter = require('../lib/geo').nearFilter;
|
||||
const geoFilter = require('../lib/geo').filter;
|
||||
const DELTA = 0.0000001;
|
||||
|
||||
describe('GeoPoint', function() {
|
||||
describe('constructor', function() {
|
||||
it('should support a valid array', function() {
|
||||
var point = new GeoPoint([-34, 150]);
|
||||
const point = new GeoPoint([-34, 150]);
|
||||
|
||||
point.lat.should.equal(-34);
|
||||
point.lng.should.equal(150);
|
||||
});
|
||||
|
||||
it('should support a valid object', function() {
|
||||
var point = new GeoPoint({lat: -34, lng: 150});
|
||||
const point = new GeoPoint({lat: -34, lng: 150});
|
||||
|
||||
point.lat.should.equal(-34);
|
||||
point.lng.should.equal(150);
|
||||
});
|
||||
|
||||
it('should support valid string geo coordinates', function() {
|
||||
var point = new GeoPoint('-34,150');
|
||||
const point = new GeoPoint('-34,150');
|
||||
|
||||
point.lat.should.equal(-34);
|
||||
point.lng.should.equal(150);
|
||||
});
|
||||
|
||||
it('should support coordinates as inline parameters', function() {
|
||||
var point = new GeoPoint(-34, 150);
|
||||
const point = new GeoPoint(-34, 150);
|
||||
|
||||
point.lat.should.equal(-34);
|
||||
point.lng.should.equal(150);
|
||||
|
@ -47,7 +44,7 @@ describe('GeoPoint', function() {
|
|||
|
||||
it('should reject invalid parameters', function() {
|
||||
/* jshint -W024 */
|
||||
var fn = function() {
|
||||
let fn = function() {
|
||||
new GeoPoint('150,-34');
|
||||
};
|
||||
fn.should.throw();
|
||||
|
@ -84,17 +81,17 @@ describe('GeoPoint', function() {
|
|||
|
||||
describe('toString()', function() {
|
||||
it('should return a string in the form "lat,lng"', function() {
|
||||
var point = new GeoPoint({lat: -34, lng: 150});
|
||||
const point = new GeoPoint({lat: -34, lng: 150});
|
||||
point.toString().should.equal('-34,150');
|
||||
});
|
||||
});
|
||||
|
||||
describe('distance calculation between two points', function() {
|
||||
var here = new GeoPoint({lat: 40.77492964101182, lng: -73.90950187151662});
|
||||
var there = new GeoPoint({lat: 40.7753227, lng: -73.909217});
|
||||
const here = new GeoPoint({lat: 40.77492964101182, lng: -73.90950187151662});
|
||||
const there = new GeoPoint({lat: 40.7753227, lng: -73.909217});
|
||||
|
||||
it('should return value in miles by default', function() {
|
||||
var distance = GeoPoint.distanceBetween(here, there);
|
||||
const distance = GeoPoint.distanceBetween(here, there);
|
||||
distance.should.be.a.Number;
|
||||
distance.should.be.approximately(0.03097916611592679, DELTA);
|
||||
});
|
||||
|
@ -109,7 +106,7 @@ describe('GeoPoint', function() {
|
|||
* - `degrees`
|
||||
*/
|
||||
|
||||
var distance = here.distanceTo(there, {type: 'radians'});
|
||||
let distance = here.distanceTo(there, {type: 'radians'});
|
||||
distance.should.be.a.Number;
|
||||
distance.should.be.approximately(0.000007825491914348416, DELTA);
|
||||
|
||||
|
@ -137,7 +134,7 @@ describe('GeoPoint', function() {
|
|||
|
||||
describe('nearFilter()', function() {
|
||||
it('should return a filter includes minDistance if where contains minDistance option', function() {
|
||||
var where = {
|
||||
const where = {
|
||||
location: {
|
||||
near: {
|
||||
lat: 40.77492964101182,
|
||||
|
@ -146,7 +143,7 @@ describe('GeoPoint', function() {
|
|||
minDistance: 100,
|
||||
},
|
||||
};
|
||||
var filter = nearFilter(where);
|
||||
const filter = nearFilter(where);
|
||||
filter[0].key.should.equal('location');
|
||||
filter[0].should.have.properties({
|
||||
key: 'location',
|
||||
|
@ -161,7 +158,7 @@ describe('GeoPoint', function() {
|
|||
|
||||
describe('filter()', function() {
|
||||
it('should be able to filter geo points via minDistance', function() {
|
||||
var points = [{
|
||||
const points = [{
|
||||
location: {
|
||||
lat: 30.283552,
|
||||
lng: 120.126048,
|
||||
|
@ -187,7 +184,7 @@ describe('GeoPoint', function() {
|
|||
lng: 121.483687,
|
||||
},
|
||||
}];
|
||||
var filter = [{
|
||||
const filter = [{
|
||||
key: 'location',
|
||||
near: {
|
||||
lat: 30.278562,
|
||||
|
@ -196,7 +193,7 @@ describe('GeoPoint', function() {
|
|||
unit: 'meters',
|
||||
minDistance: 10000,
|
||||
}];
|
||||
var results = geoFilter(points, filter);
|
||||
const results = geoFilter(points, filter);
|
||||
results.length.should.be.equal(3);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -1,10 +1,11 @@
|
|||
// Copyright IBM Corp. 2016. All Rights Reserved.
|
||||
// Copyright IBM Corp. 2016,2019. All Rights Reserved.
|
||||
// Node module: loopback-datasource-juggler
|
||||
// This file is licensed under the MIT License.
|
||||
// License text available at https://opensource.org/licenses/MIT
|
||||
|
||||
'use strict';
|
||||
|
||||
var fmt = require('util').format;
|
||||
const fmt = require('util').format;
|
||||
|
||||
exports.describeIf = function describeIf(cond, name, fn) {
|
||||
if (cond)
|
||||
|
|
|
@ -1,10 +1,11 @@
|
|||
// Copyright IBM Corp. 2016. All Rights Reserved.
|
||||
// Copyright IBM Corp. 2016,2019. All Rights Reserved.
|
||||
// Node module: loopback-datasource-juggler
|
||||
// This file is licensed under the MIT License.
|
||||
// License text available at https://opensource.org/licenses/MIT
|
||||
|
||||
'use strict';
|
||||
|
||||
var traverse = require('traverse');
|
||||
const traverse = require('traverse');
|
||||
|
||||
exports.ContextRecorder = ContextRecorder;
|
||||
exports.deepCloneToObject = deepCloneToObject;
|
||||
|
@ -15,17 +16,25 @@ function ContextRecorder(initialValue) {
|
|||
return new ContextRecorder(initialValue);
|
||||
}
|
||||
this.records = initialValue;
|
||||
};
|
||||
}
|
||||
|
||||
ContextRecorder.prototype.recordAndNext = function(transformFm) {
|
||||
var self = this;
|
||||
const self = this;
|
||||
return function(context, next) {
|
||||
if (typeof transformFm === 'function') {
|
||||
transformFm(context);
|
||||
}
|
||||
|
||||
context = deepCloneToObject(context);
|
||||
context.hookState.test = true;
|
||||
if (Array.isArray(context)) {
|
||||
context = context.map(ctx => {
|
||||
const ctxCopy = deepCloneToObject(ctx);
|
||||
ctxCopy.hookState.test = true;
|
||||
return ctxCopy;
|
||||
});
|
||||
} else {
|
||||
context = deepCloneToObject(context);
|
||||
context.hookState.test = true;
|
||||
}
|
||||
|
||||
if (typeof self.records === 'string') {
|
||||
self.records = context;
|
||||
|
@ -36,7 +45,11 @@ ContextRecorder.prototype.recordAndNext = function(transformFm) {
|
|||
self.records = [self.records];
|
||||
}
|
||||
|
||||
self.records.push(context);
|
||||
if (Array.isArray(context)) {
|
||||
self.records.push(...context);
|
||||
} else {
|
||||
self.records.push(context);
|
||||
}
|
||||
next();
|
||||
};
|
||||
};
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
// Copyright IBM Corp. 2016. All Rights Reserved.
|
||||
// Copyright IBM Corp. 2016,2019. All Rights Reserved.
|
||||
// Node module: loopback-datasource-juggler
|
||||
// This file is licensed under the MIT License.
|
||||
// License text available at https://opensource.org/licenses/MIT
|
||||
|
||||
'use strict';
|
||||
|
||||
module.exports = HookMonitor;
|
||||
|
@ -13,15 +14,15 @@ function HookMonitor(opts) {
|
|||
|
||||
this.options = opts || {};
|
||||
this.names = [];
|
||||
};
|
||||
}
|
||||
|
||||
HookMonitor.prototype.install = function(ObservedModel, hookNames) {
|
||||
var monitor = this;
|
||||
const monitor = this;
|
||||
this.names = [];
|
||||
ObservedModel._notify = ObservedModel.notifyObserversOf;
|
||||
ObservedModel.notifyObserversOf = function(operation, context, callback) {
|
||||
if (!Array.isArray(hookNames) || hookNames.indexOf(operation) !== -1) {
|
||||
var item = monitor.options.includeModelName ?
|
||||
const item = monitor.options.includeModelName ?
|
||||
ObservedModel.modelName + ':' + operation :
|
||||
operation;
|
||||
monitor.names.push(item);
|
||||
|
|
|
@ -0,0 +1,24 @@
|
|||
'use strict';
|
||||
|
||||
const assert = require('assert');
|
||||
|
||||
/**
|
||||
* Helper function that when called should return the current instance of the modelBuilder
|
||||
* @param {function: ModelBuilder} getBuilder
|
||||
*/
|
||||
const createTestSetupForParentRef = (getBuilder) => {
|
||||
assert.strictEqual(typeof getBuilder, 'function', 'Missing getter function for model builder');
|
||||
const settingProperty = 'parentRef';
|
||||
beforeEach('enabling parentRef for given modelBuilder', () => {
|
||||
const modelBuilder = getBuilder();
|
||||
assert(modelBuilder && typeof modelBuilder === 'object', 'Invalid modelBuilder instance');
|
||||
modelBuilder.settings[settingProperty] = true;
|
||||
});
|
||||
afterEach('Disabling parentRef for given modelBuilder', () => {
|
||||
const modelBuilder = getBuilder();
|
||||
assert(modelBuilder && typeof modelBuilder === 'object', 'Invalid modelBuilder instance');
|
||||
modelBuilder.settings[settingProperty] = false;
|
||||
});
|
||||
};
|
||||
|
||||
module.exports = createTestSetupForParentRef;
|
|
@ -1,11 +1,11 @@
|
|||
// Copyright IBM Corp. 2016. All Rights Reserved.
|
||||
// Copyright IBM Corp. 2016,2019. All Rights Reserved.
|
||||
// Node module: loopback-datasource-juggler
|
||||
// This file is licensed under the MIT License.
|
||||
// License text available at https://opensource.org/licenses/MIT
|
||||
|
||||
'use strict';
|
||||
|
||||
var lastId = 0;
|
||||
let lastId = 0;
|
||||
|
||||
exports.next = function() {
|
||||
lastId++;
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// Copyright IBM Corp. 2013,2016. All Rights Reserved.
|
||||
// Copyright IBM Corp. 2013,2019. All Rights Reserved.
|
||||
// Node module: loopback-datasource-juggler
|
||||
// This file is licensed under the MIT License.
|
||||
// License text available at https://opensource.org/licenses/MIT
|
||||
|
@ -7,14 +7,14 @@
|
|||
|
||||
// This test written in mocha+should.js
|
||||
/* global getSchema:false */
|
||||
var should = require('./init.js');
|
||||
const should = require('./init.js');
|
||||
|
||||
var j = require('../'),
|
||||
const j = require('../'),
|
||||
Schema = j.Schema,
|
||||
AbstractClass = j.AbstractClass,
|
||||
Hookable = j.Hookable,
|
||||
Hookable = j.Hookable;
|
||||
|
||||
db, User;
|
||||
let db, User;
|
||||
|
||||
describe('hooks', function() {
|
||||
before(function(done) {
|
||||
|
@ -43,7 +43,7 @@ describe('hooks', function() {
|
|||
});
|
||||
|
||||
it('should be triggered on create', function(done) {
|
||||
var user;
|
||||
let user;
|
||||
User.afterInitialize = function() {
|
||||
if (this.name === 'Nickolay') {
|
||||
this.name += ' Rozental';
|
||||
|
@ -70,7 +70,7 @@ describe('hooks', function() {
|
|||
should.fail('This should not be called');
|
||||
next();
|
||||
};
|
||||
var u = new User;
|
||||
const u = new User;
|
||||
});
|
||||
|
||||
it('should be triggered on new+save', function(done) {
|
||||
|
@ -79,7 +79,7 @@ describe('hooks', function() {
|
|||
});
|
||||
|
||||
it('afterCreate should not be triggered on failed create', function(done) {
|
||||
var old = User.dataSource.connector.create;
|
||||
const old = User.dataSource.connector.create;
|
||||
User.dataSource.connector.create = function(modelName, id, cb) {
|
||||
cb(new Error('error'));
|
||||
};
|
||||
|
@ -99,7 +99,7 @@ describe('hooks', function() {
|
|||
next(new Error('fail in beforeCreate'));
|
||||
};
|
||||
|
||||
var old = User.dataSource.connector.create;
|
||||
const old = User.dataSource.connector.create;
|
||||
User.dataSource.connector.create = function(modelName, id, cb) {
|
||||
throw new Error('shouldn\'t be called');
|
||||
};
|
||||
|
@ -259,7 +259,7 @@ describe('hooks', function() {
|
|||
should.fail('afterUpdate shouldn\'t be called');
|
||||
};
|
||||
User.create(function(err, user) {
|
||||
var save = User.dataSource.connector.save;
|
||||
const save = User.dataSource.connector.save;
|
||||
User.dataSource.connector.save = function(modelName, id, cb) {
|
||||
User.dataSource.connector.save = save;
|
||||
cb(new Error('Error'));
|
||||
|
@ -276,7 +276,7 @@ describe('hooks', function() {
|
|||
afterEach(removeHooks('Destroy'));
|
||||
|
||||
it('should be triggered on destroy', function(done) {
|
||||
var hook = 'not called';
|
||||
let hook = 'not called';
|
||||
User.beforeDestroy = function(next) {
|
||||
hook = 'called';
|
||||
next();
|
||||
|
@ -291,7 +291,7 @@ describe('hooks', function() {
|
|||
});
|
||||
|
||||
it('should not trigger after-hook on failed destroy', function(done) {
|
||||
var destroy = User.dataSource.connector.destroy;
|
||||
const destroy = User.dataSource.connector.destroy;
|
||||
User.dataSource.connector.destroy = function(modelName, id, cb) {
|
||||
cb(new Error('error'));
|
||||
};
|
||||
|
@ -308,7 +308,7 @@ describe('hooks', function() {
|
|||
});
|
||||
|
||||
describe('lifecycle', function() {
|
||||
var life = [], user;
|
||||
let life = [], user;
|
||||
before(function(done) {
|
||||
User.beforeSave = function(d) {
|
||||
life.push('beforeSave');
|
||||
|
@ -379,7 +379,7 @@ describe('hooks', function() {
|
|||
});
|
||||
|
||||
it('should describe new+save sequence', function(done) {
|
||||
var u = new User;
|
||||
const u = new User;
|
||||
u.save(function() {
|
||||
life.should.eql([
|
||||
'afterInitialize',
|
||||
|
@ -411,7 +411,8 @@ describe('hooks', function() {
|
|||
it('should describe isValid sequence', function(done) {
|
||||
should.not.exist(
|
||||
user.constructor._validations,
|
||||
'Expected user to have no validations, but she have');
|
||||
'Expected user to have no validations, but she have',
|
||||
);
|
||||
user.isValid(function(valid) {
|
||||
valid.should.be.true;
|
||||
life.should.eql([
|
||||
|
@ -435,7 +436,8 @@ describe('hooks', function() {
|
|||
});
|
||||
|
||||
function addHooks(name, done) {
|
||||
var called = false, random = String(Math.floor(Math.random() * 1000));
|
||||
const random = String(Math.floor(Math.random() * 1000));
|
||||
let called = false;
|
||||
User['before' + name] = function(next, data) {
|
||||
called = true;
|
||||
data.email = random;
|
||||
|
|
1536
test/include.test.js
1536
test/include.test.js
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue