Compare commits
635 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 |
|
@ -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
|
||||
}
|
||||
|
|
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
|
||||
|
|
1061
CHANGES.md
1061
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
|
||||
|
|
56
lib/list.js
56
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);
|
||||
|
@ -102,7 +131,7 @@ List.prototype.toObject = function(onlySchema, removeHidden, removeProtected) {
|
|||
* Some modules such as `should` checks prototype for comparison
|
||||
*/
|
||||
List.prototype.toArray = function() {
|
||||
var items = [];
|
||||
const items = [];
|
||||
this.forEach(function(item) {
|
||||
items.push(item);
|
||||
});
|
||||
|
@ -117,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');
|
||||
}
|
||||
|
216
lib/model.js
216
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;
|
||||
|
@ -419,7 +442,7 @@ ModelBaseClass.toString = function() {
|
|||
*/
|
||||
ModelBaseClass.prototype.toObject = function(onlySchema, removeHidden, removeProtected) {
|
||||
if (typeof onlySchema === 'object' && onlySchema != null) {
|
||||
var options = onlySchema;
|
||||
const options = onlySchema;
|
||||
onlySchema = options.onlySchema;
|
||||
removeHidden = options.removeHidden;
|
||||
removeProtected = options.removeProtected;
|
||||
|
@ -427,24 +450,24 @@ ModelBaseClass.prototype.toObject = function(onlySchema, removeHidden, removePro
|
|||
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];
|
||||
|
||||
|
@ -480,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;
|
||||
|
@ -515,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;
|
||||
|
@ -527,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') {
|
||||
|
@ -548,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];
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -578,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() {
|
||||
|
@ -601,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];
|
||||
}
|
||||
};
|
||||
|
@ -612,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];
|
||||
}
|
||||
|
@ -623,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);
|
||||
|
@ -645,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
|
||||
|
@ -656,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;
|
||||
}
|
||||
|
@ -788,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
|
||||
|
@ -797,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
|
||||
|
@ -840,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.21.1",
|
||||
"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": {
|
||||
"@types/node": "^10.0.8",
|
||||
"@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": {
|
||||
"async": "^2.6.0",
|
||||
"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,21 +151,21 @@ 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) {
|
||||
var mockConnector = {
|
||||
const mockConnector = {
|
||||
name: 'loopback-connector-mock',
|
||||
initialize: function(ds, cb) {
|
||||
ds.connector = mockConnector;
|
||||
|
@ -169,7 +178,7 @@ describe('DataSource', function() {
|
|||
});
|
||||
},
|
||||
};
|
||||
var dataSource = new DataSource(mockConnector);
|
||||
const dataSource = new DataSource(mockConnector);
|
||||
// DataSource is instantiated
|
||||
// connected: false, connecting: false, initialized: false
|
||||
dataSource.connected.should.be.false();
|
||||
|
@ -211,7 +220,7 @@ describe('DataSource', function() {
|
|||
});
|
||||
|
||||
it('should set states correctly with deferred connect', function(done) {
|
||||
var mockConnector = {
|
||||
const mockConnector = {
|
||||
name: 'loopback-connector-mock',
|
||||
initialize: function(ds, cb) {
|
||||
ds.connector = mockConnector;
|
||||
|
@ -227,7 +236,7 @@ describe('DataSource', function() {
|
|||
});
|
||||
},
|
||||
};
|
||||
var dataSource = new DataSource(mockConnector);
|
||||
const dataSource = new DataSource(mockConnector);
|
||||
// DataSource is instantiated
|
||||
// connected: false, connecting: false, initialized: false
|
||||
dataSource.connected.should.be.false();
|
||||
|
@ -267,7 +276,7 @@ describe('DataSource', function() {
|
|||
});
|
||||
|
||||
it('should set states correctly with lazyConnect = true', function(done) {
|
||||
var mockConnector = {
|
||||
const mockConnector = {
|
||||
name: 'loopback-connector-mock',
|
||||
initialize: function(ds, cb) {
|
||||
ds.connector = mockConnector;
|
||||
|
@ -282,7 +291,7 @@ describe('DataSource', function() {
|
|||
});
|
||||
},
|
||||
};
|
||||
var dataSource = new DataSource(mockConnector, {lazyConnect: true});
|
||||
const dataSource = new DataSource(mockConnector, {lazyConnect: true});
|
||||
// DataSource is instantiated
|
||||
// connected: false, connecting: false, initialized: false
|
||||
dataSource.connected.should.be.false();
|
||||
|
@ -321,6 +330,31 @@ describe('DataSource', function() {
|
|||
});
|
||||
});
|
||||
|
||||
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'});
|
||||
|
@ -352,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
|
@ -1,74 +1,94 @@
|
|||
// 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 assert = require('assert');
|
||||
var should = require('should');
|
||||
const assert = require('assert');
|
||||
const should = require('should');
|
||||
|
||||
var includeUtils = require('../lib/include_utils');
|
||||
const includeUtils = require('../lib/include_utils');
|
||||
|
||||
describe('include_util', function() {
|
||||
describe('#buildOneToOneIdentityMapWithOrigKeys', function() {
|
||||
it('should return an object with keys', function() {
|
||||
var objs = [
|
||||
{id: 11, letter: 'A'},
|
||||
{id: 22, letter: 'B'},
|
||||
const objs = [
|
||||
{id: 11, letter: 'A'},
|
||||
{id: 22, letter: 'B'},
|
||||
];
|
||||
var result = includeUtils.buildOneToOneIdentityMapWithOrigKeys(objs, 'id');
|
||||
const result = includeUtils.buildOneToOneIdentityMapWithOrigKeys(objs, 'id');
|
||||
result.get(11).should.be.ok;
|
||||
result.get(22).should.be.ok;
|
||||
});
|
||||
|
||||
it('should report errors if id is missing', function() {
|
||||
const objs = [
|
||||
{letter: 'A'},
|
||||
{id: 22, letter: 'B'},
|
||||
];
|
||||
function build() {
|
||||
includeUtils.buildOneToOneIdentityMapWithOrigKeys(objs, 'id');
|
||||
}
|
||||
build.should.throw(/ID property "id" is missing/);
|
||||
});
|
||||
|
||||
it('should overwrite keys in case of collision', function() {
|
||||
var objs = [
|
||||
const objs = [
|
||||
{id: 11, letter: 'A'},
|
||||
{id: 22, letter: 'B'},
|
||||
{id: 33, letter: 'C'},
|
||||
{id: 11, letter: 'HA!'},
|
||||
];
|
||||
|
||||
var result = includeUtils.buildOneToOneIdentityMapWithOrigKeys(objs, 'id');
|
||||
const result = includeUtils.buildOneToOneIdentityMapWithOrigKeys(objs, 'id');
|
||||
result.getKeys().should.containEql(11);
|
||||
result.getKeys().should.containEql(22);
|
||||
result.getKeys().should.containEql(33);
|
||||
result.get(11)['letter'].should.equal('HA!');
|
||||
result.get(33)['letter'].should.equal('C');
|
||||
});
|
||||
});
|
||||
describe('#buildOneToOneIdentityMapWithOrigKeys', function() {
|
||||
it('should return an object with keys', function() {
|
||||
var objs = [
|
||||
|
||||
it('should return an object with no additional keys', function() {
|
||||
const objs = [
|
||||
{id: 11, letter: 'A'},
|
||||
{id: 22, letter: 'B'},
|
||||
];
|
||||
var result = includeUtils.buildOneToOneIdentityMapWithOrigKeys(objs, 'id');
|
||||
result.get(11).should.be.ok;
|
||||
result.get(22).should.be.ok;
|
||||
result.getKeys().should.have.lengthOf(2); // no additional properties
|
||||
const result = includeUtils.buildOneToOneIdentityMapWithOrigKeys(objs, 'id');
|
||||
result.getKeys().should.eql([11, 22]); // no additional properties
|
||||
});
|
||||
});
|
||||
|
||||
describe('#buildOneToManyIdentityMap', function() {
|
||||
it('should return an object with keys', function() {
|
||||
var objs = [
|
||||
const objs = [
|
||||
{id: 11, letter: 'A'},
|
||||
{id: 22, letter: 'B'},
|
||||
];
|
||||
var result = includeUtils.buildOneToManyIdentityMapWithOrigKeys(objs, 'id');
|
||||
const result = includeUtils.buildOneToManyIdentityMapWithOrigKeys(objs, 'id');
|
||||
result.exist(11).should.be.true;
|
||||
result.exist(22).should.be.true;
|
||||
});
|
||||
|
||||
it('should report errors if id is missing', function() {
|
||||
const objs = [
|
||||
{letter: 'A'},
|
||||
{id: 22, letter: 'B'},
|
||||
];
|
||||
function build() {
|
||||
includeUtils.buildOneToManyIdentityMapWithOrigKeys(objs, 'id');
|
||||
}
|
||||
build.should.throw(/ID property "id" is missing/);
|
||||
});
|
||||
|
||||
it('should collect keys in case of collision', function() {
|
||||
var objs = [
|
||||
const objs = [
|
||||
{'fk_id': 11, letter: 'A'},
|
||||
{'fk_id': 22, letter: 'B'},
|
||||
{'fk_id': 33, letter: 'C'},
|
||||
{'fk_id': 11, letter: 'HA!'},
|
||||
];
|
||||
|
||||
var result = includeUtils.buildOneToManyIdentityMapWithOrigKeys(objs, 'fk_id');
|
||||
const result = includeUtils.buildOneToManyIdentityMapWithOrigKeys(objs, 'fk_id');
|
||||
result.get(11)[0]['letter'].should.equal('A');
|
||||
result.get(11)[1]['letter'].should.equal('HA!');
|
||||
result.get(33)[0]['letter'].should.equal('C');
|
||||
|
@ -78,7 +98,7 @@ describe('include_util', function() {
|
|||
|
||||
describe('KVMap', function() {
|
||||
it('should allow to set and get value with key string', function() {
|
||||
var map = new includeUtils.KVMap();
|
||||
const map = new includeUtils.KVMap();
|
||||
map.set('name', 'Alex');
|
||||
map.set('gender', true);
|
||||
map.set('age', 25);
|
||||
|
@ -87,7 +107,7 @@ describe('KVMap', function() {
|
|||
map.get('age').should.be.equal(25);
|
||||
});
|
||||
it('should allow to set and get value with arbitrary key type', function() {
|
||||
var map = new includeUtils.KVMap();
|
||||
const map = new includeUtils.KVMap();
|
||||
map.set('name', 'Alex');
|
||||
map.set(true, 'male');
|
||||
map.set(false, false);
|
||||
|
@ -98,12 +118,12 @@ describe('KVMap', function() {
|
|||
map.get({isTrue: 'yes'}).should.be.equal(25);
|
||||
});
|
||||
it('should not allow to get values with [] operator', function() {
|
||||
var map = new includeUtils.KVMap();
|
||||
const map = new includeUtils.KVMap();
|
||||
map.set('name', 'Alex');
|
||||
(map['name'] === undefined).should.be.equal(true);
|
||||
});
|
||||
it('should provide .exist() method for checking if key presented', function() {
|
||||
var map = new includeUtils.KVMap();
|
||||
const map = new includeUtils.KVMap();
|
||||
map.set('one', 1);
|
||||
map.set(2, 'two');
|
||||
map.set(true, 'true');
|
||||
|
@ -113,24 +133,24 @@ describe('KVMap', function() {
|
|||
map.exist('two').should.be.false;
|
||||
});
|
||||
it('should return array of original keys with .getKeys()', function() {
|
||||
var map = new includeUtils.KVMap();
|
||||
const map = new includeUtils.KVMap();
|
||||
map.set('one', 1);
|
||||
map.set(2, 'two');
|
||||
map.set(true, 'true');
|
||||
var keys = map.getKeys();
|
||||
const keys = map.getKeys();
|
||||
keys.should.containEql('one');
|
||||
keys.should.containEql(2);
|
||||
keys.should.containEql(true);
|
||||
});
|
||||
it('should allow to store and fetch arrays', function() {
|
||||
var map = new includeUtils.KVMap();
|
||||
const map = new includeUtils.KVMap();
|
||||
map.set(1, [1, 2, 3]);
|
||||
map.set(2, [2, 3, 4]);
|
||||
var valueOne = map.get(1);
|
||||
const valueOne = map.get(1);
|
||||
valueOne.should.be.eql([1, 2, 3]);
|
||||
valueOne.push(99);
|
||||
map.set(1, valueOne);
|
||||
var valueOneUpdated = map.get(1);
|
||||
const valueOneUpdated = map.get(1);
|
||||
valueOneUpdated.should.be.eql([1, 2, 3, 99]);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -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
|
||||
|
@ -18,8 +18,8 @@ module.exports = require('should');
|
|||
}
|
||||
*/
|
||||
|
||||
var ModelBuilder = require('../').ModelBuilder;
|
||||
var Schema = require('../').Schema;
|
||||
const ModelBuilder = require('../').ModelBuilder;
|
||||
const Schema = require('../').Schema;
|
||||
|
||||
if (!('getSchema' in global)) {
|
||||
global.getSchema = function(connector, settings) {
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue