Compare commits
1241 Commits
Author | SHA1 | Date |
---|---|---|
|
58585c1526 | |
|
7abd2ecddc | |
|
819e1845c7 | |
|
394d16a96f | |
|
29f45692e9 | |
|
01d8584a7f | |
|
7fce7dd559 | |
|
331cca66dd | |
|
a25e2da920 | |
|
84618aead6 | |
|
3f910f9d2b | |
|
2f4ff8b6a9 | |
|
7bc1d917dd | |
|
99cd30a7a4 | |
|
de40642e89 | |
|
8233e2abed | |
|
685d1da15c | |
|
163499308f | |
|
c92e5862d0 | |
|
dd40c27e8a | |
|
c2c892b071 | |
|
260acdd898 | |
|
9ae71251ae | |
|
d35149e2b7 | |
|
034cb0048b | |
|
ca091d0342 | |
|
a84721b8d8 | |
|
602602f69a | |
|
26d4b30634 | |
|
b7a7963223 | |
|
12ebf9c5e6 | |
|
23b7ca8cff | |
|
feaecba71c | |
|
a206198e31 | |
|
13f2ce6b98 | |
|
68bff40ba7 | |
|
79a06a587c | |
|
b83f9e68e9 | |
|
a7efa3576b | |
|
edbdf8520b | |
|
1f5a4cd072 | |
|
365f27b911 | |
|
99ff788169 | |
|
8de60b558e | |
|
5bd2b4a863 | |
|
5bdf70874f | |
|
ba8b93e692 | |
|
b6b240afc9 | |
|
c4fac50863 | |
|
d7083ec8f2 | |
|
911f29e8df | |
|
5673cbd52b | |
|
714dc55ccd | |
|
838905877f | |
|
138cd90d6c | |
|
52cd087aff | |
|
ed7cabb668 | |
|
dee64a9b4c | |
|
a35e8d18c2 | |
|
33d06b5c71 | |
|
748fe22bcb | |
|
901486f0d3 | |
|
60959e36c3 | |
|
b603283dc0 | |
|
9d47253316 | |
|
03c1683a83 | |
|
07d3e1bbf7 | |
|
e9af46e836 | |
|
3a50b1356e | |
|
726476f8e0 | |
|
5c9e59845f | |
|
3be58c9446 | |
|
df89ae59e3 | |
|
f191c8a5dd | |
|
3268b036d5 | |
|
9b96f69098 | |
|
14abfbd6dc | |
|
8e642d7686 | |
|
be3faa1ee2 | |
|
fbd4b63b24 | |
|
0c9aca3b36 | |
|
da0863edf0 | |
|
70520a7812 | |
|
b4c23eb6d1 | |
|
ad8b95b2eb | |
|
e24936078c | |
|
4f8af05cdf | |
|
7a847a04d1 | |
|
da54829f83 | |
|
2eae64f3cc | |
|
4557e2e10f | |
|
469d4802bb | |
|
76fe5ad1c7 | |
|
f80a351225 | |
|
82247a34ae | |
|
9fa099484f | |
|
15a9e785a7 | |
|
01a942cae0 | |
|
3ab268dc3c | |
|
1dd3546c3f | |
|
2137f28948 | |
|
244ec17e60 | |
|
ea84ff4314 | |
|
f937e63d25 | |
|
b6f88b6a2a | |
|
603ebced82 | |
|
75482a9acc | |
|
d6f6910980 | |
|
8a3880289d | |
|
ae8597883a | |
|
d584420397 | |
|
ae9695fe3c | |
|
92c26b4100 | |
|
ef397c07e9 | |
|
7454c4a150 | |
|
dac2d47312 | |
|
23e1a4a4c2 | |
|
cdec276338 | |
|
a2bae7dabf | |
|
b7e9f4d00a | |
|
5f1161a8ab | |
|
584a5ccb82 | |
|
28d4007fdc | |
|
2667d3b7d9 | |
|
7926b52480 | |
|
da06e6ea60 | |
|
30751df17d | |
|
bbd63ce67e | |
|
9846fceaa6 | |
|
ef210ef56a | |
|
f2b02920ba | |
|
42430be43b | |
|
8562200853 | |
|
3469bded5a | |
|
8b6cad7d66 | |
|
40bb3c0b79 | |
|
757f48a539 | |
|
1ea6ceb0d1 | |
|
ac34f8141e | |
|
ea3cdb1a71 | |
|
90564e3100 | |
|
90e1485d8c | |
|
19e9d633bc | |
|
9d5899cab2 | |
|
1a4a6ae780 | |
|
7cbc8a86ee | |
|
8306749436 | |
|
518e3ca1ca | |
|
415dfda681 | |
|
8f43d9b659 | |
|
354235247b | |
|
861cc5c448 | |
|
8716469dc2 | |
|
fa18610f96 | |
|
f7ded61ad5 | |
|
bab4c90553 | |
|
3bd71ff26f | |
|
0d58339bb8 | |
|
23e76f905e | |
|
363bdcea5d | |
|
6959b9c620 | |
|
3b121482cf | |
|
76cdc59461 | |
|
1c197d8b17 | |
|
2f7ea50341 | |
|
6729c8a0ec | |
|
cdfc71ec77 | |
|
15da93ef83 | |
|
7184fb054a | |
|
b6cd522d44 | |
|
07333dfe8b | |
|
f14ae8af4b | |
|
025b22a9af | |
|
4908768c02 | |
|
3dea22d63e | |
|
ae3cf25e77 | |
|
c905f025df | |
|
1977e5470b | |
|
ab90d50eff | |
|
fb725e9ff0 | |
|
8a126f623d | |
|
8b29d8db01 | |
|
d395dbb24d | |
|
52c6532add | |
|
0b9cee7f3b | |
|
efc5d1d6f8 | |
|
28debb4c75 | |
|
a927649bc8 | |
|
521bfba219 | |
|
dfa614e025 | |
|
ff623d6a0d | |
|
f6b2508dfd | |
|
2f6dd870a6 | |
|
46aaa540bd | |
|
9e928208d0 | |
|
9cf7686a36 | |
|
d72ced71e0 | |
|
04b84b5e1b | |
|
9b66ad190e | |
|
4fb97fcd04 | |
|
66abcebe8a | |
|
abf82d7eca | |
|
58c59cad72 | |
|
2b4ad689ad | |
|
a229a7ac58 | |
|
888e66bf6a | |
|
2a233c7274 | |
|
b7e8f5ff66 | |
|
59b61d5627 | |
|
07958c4076 | |
|
80e2b2112d | |
|
d274a48192 | |
|
d2b09ebf94 | |
|
c61a47e88e | |
|
66af10f401 | |
|
69f010e4ca | |
|
8e8fe00564 | |
|
15f1a85fdc | |
|
c5eeb88a93 | |
|
253ecfcbfb | |
|
9df18e2a75 | |
|
8dce904fc6 | |
|
d31b9d8b97 | |
|
2f993718f0 | |
|
e20550b680 | |
|
316ebce660 | |
|
c03b0a0440 | |
|
e486faf93a | |
|
a883c06bbc | |
|
9e34360aae | |
|
e8a401c347 | |
|
a97aed958c | |
|
936be6bbff | |
|
236df1d649 | |
|
dde1101793 | |
|
86ef5a48a9 | |
|
69cb28ade1 | |
|
3b59084cd0 | |
|
340db473ed | |
|
492d569903 | |
|
99d56bba79 | |
|
8b8fc962c7 | |
|
df78022886 | |
|
abf7bbaa1e | |
|
b50cbb3cfd | |
|
713a2775f2 | |
|
21870e41ce | |
|
b4441976eb | |
|
37ae2919e2 | |
|
bc2f0fd705 | |
|
8cd336407c | |
|
e34b63ea79 | |
|
cbf3fe162d | |
|
00b9903fac | |
|
0e519f7559 | |
|
0f759fe0aa | |
|
6ede40fee4 | |
|
6ffad9f7d6 | |
|
d29bec72a8 | |
|
cb20ae6575 | |
|
31ea05783a | |
|
2789a92899 | |
|
1d0f914867 | |
|
e6da14dcd1 | |
|
7b4b0d3140 | |
|
ac49c3ecd2 | |
|
3e6a6367cc | |
|
91e255d850 | |
|
1d2d6cb4e1 | |
|
b97a28db7b | |
|
3a1dca1e87 | |
|
70e859fa66 | |
|
ed951a6a61 | |
|
e9b42dcd74 | |
|
f45c92c1f8 | |
|
82f11f0235 | |
|
49cb048a45 | |
|
837240b7f9 | |
|
12a502b097 | |
|
30d27f00c4 | |
|
00a0693db0 | |
|
a0659aefa0 | |
|
5f1888b41a | |
|
4bc39fdec1 | |
|
caea97e3f5 | |
|
15b1335250 | |
|
9b8123a14c | |
|
ed1c5079f6 | |
|
53942e621a | |
|
a1321b9ecb | |
|
b84c5201b1 | |
|
f8d7ca9474 | |
|
53767a6995 | |
|
bc9c4defd1 | |
|
615b95dde9 | |
|
5dbc0cab83 | |
|
775cbbf2b9 | |
|
a4eb2f0377 | |
|
c021fc9abf | |
|
d9eac14926 | |
|
2db05f56e1 | |
|
ba09182cea | |
|
be09d5774e | |
|
21ea203a58 | |
|
2fcbb1bd77 | |
|
b4c5e22403 | |
|
3f39054504 | |
|
9a6d0d83ca | |
|
f30fd1819c | |
|
6aef060d52 | |
|
b3b5dc2cd5 | |
|
5665ff69d5 | |
|
bb577e61b5 | |
|
6dfa9b859c | |
|
bc50aa7c4d | |
|
b810bd88ca | |
|
3ebf47c4ea | |
|
5f6a5d5201 | |
|
e647559995 | |
|
de008c40e3 | |
|
c748ad1d26 | |
|
a935fabcd1 | |
|
2f95d901bd | |
|
6186b57659 | |
|
d7554634fc | |
|
e3addf9f93 | |
|
e30c2c8dbd | |
|
8d4de43c8a | |
|
3b92a09e41 | |
|
26bd2225f1 | |
|
f60d4b0cca | |
|
2cd612c4c3 | |
|
07cc90e40f | |
|
d04a6c13d1 | |
|
2be3b8be62 | |
|
92e05e6537 | |
|
2691470504 | |
|
74d429d52d | |
|
fe82f6ac79 | |
|
816b948ab0 | |
|
e3448fad03 | |
|
9a58695740 | |
|
5aa2d71a23 | |
|
cc5557bd11 | |
|
a0de6bc760 | |
|
06428247ad | |
|
05898ed9f6 | |
|
454fd0f1b2 | |
|
1e9ac69f2c | |
|
d2a594fc08 | |
|
8487ee1bc0 | |
|
8c95930cd6 | |
|
8031e54fb4 | |
|
76acf333fd | |
|
261dd1c865 | |
|
c80aa9abe8 | |
|
35e032d1df | |
|
a0f1d4a979 | |
|
dd23a14b24 | |
|
7835a43cce | |
|
c884c62b3b | |
|
c24e498191 | |
|
6e6a3df9f0 | |
|
f13d50ea3f | |
|
0ba09a1c4f | |
|
b9929b83fe | |
|
b1cd7eff5c | |
|
8d1690e9b2 | |
|
259c0b9cc6 | |
|
4c53abad32 | |
|
429d13e537 | |
|
a25df6ac36 | |
|
7292d206e5 | |
|
4c665cd6f7 | |
|
74c6776575 | |
|
c83f82d5a0 | |
|
e026b4c4af | |
|
095ddd654e | |
|
9bcf42488d | |
|
280228960a | |
|
04336a60cf | |
|
b1766518a8 | |
|
f1f7bf2031 | |
|
6d65afc35f | |
|
c6e8779a06 | |
|
df1c8dc00e | |
|
9fe64d412c | |
|
8919115439 | |
|
df394a613b | |
|
670a328bc3 | |
|
bf931b50c2 | |
|
d94327a8f4 | |
|
376315b7a4 | |
|
2946c5b489 | |
|
39daef79d8 | |
|
cdbe715acc | |
|
a50b3db6e9 | |
|
33884062f4 | |
|
b01cd74b19 | |
|
c7b88e9b8e | |
|
65a358656f | |
|
92654f646b | |
|
dcfda58fda | |
|
36303b3b63 | |
|
f4ec02b316 | |
|
baeb76e911 | |
|
3a08b9d9ae | |
|
251ced811b | |
|
d2b4d567d9 | |
|
ad3e1e8d8a | |
|
b6f9e92ef8 | |
|
d19e189fd7 | |
|
a456700ea2 | |
|
af4f4dfa87 | |
|
abdd5675b0 | |
|
002137dd5d | |
|
b328934b6c | |
|
c555dedfeb | |
|
19048cd716 | |
|
2f867576b6 | |
|
606880bf79 | |
|
a9611a0430 | |
|
1d4ad278c9 | |
|
1b7858a857 | |
|
770f11b3a6 | |
|
89503bb233 | |
|
89a964e919 | |
|
b30fbf8c1e | |
|
d54d769477 | |
|
f921a4f630 | |
|
5578ab4ea7 | |
|
a1817a40e6 | |
|
b9f0284a28 | |
|
b07bdfc71a | |
|
28d457d357 | |
|
8b75767daf | |
|
ab11205fa0 | |
|
23949703b0 | |
|
063e0f173f | |
|
071f93762c | |
|
15251880a1 | |
|
8946516614 | |
|
1e8b8a041b | |
|
ad1777fcd3 | |
|
6f491dc756 | |
|
fa7c38cad8 | |
|
464610ef0a | |
|
814c55c7cd | |
|
ac955826f5 | |
|
a90dc0eac4 | |
|
bca631518b | |
|
ad4edc6a15 | |
|
74c43ca358 | |
|
f1fa976f50 | |
|
dd7167b533 | |
|
ca4ede014e | |
|
6b83b6f4d1 | |
|
e3c276720f | |
|
6478c73dde | |
|
78c5b12740 | |
|
6aea50ad27 | |
|
0c2bb81dac | |
|
40286fcd28 | |
|
c3103a2077 | |
|
1ed385e393 | |
|
39555a010a | |
|
85a29caaa5 | |
|
5539ef7ac6 | |
|
313925a5e7 | |
|
612cf05fdf | |
|
19e7a47489 | |
|
58b3c4f148 | |
|
d49774c590 | |
|
5fcad43aab | |
|
df4d89279a | |
|
acb667b73d | |
|
5b44c08f7a | |
|
04523260c2 | |
|
a5ba7408ff | |
|
413f31631e | |
|
aa20180c4f | |
|
344441d48d | |
|
caf9d09274 | |
|
853a41641b | |
|
5369cf3d54 | |
|
91ab3e9162 | |
|
ef0257e338 | |
|
de4718d5b8 | |
|
98eb783a80 | |
|
2aa4d20394 | |
|
0b871d1de1 | |
|
9c94b9344b | |
|
3fba79e725 | |
|
78c4bfbc2d | |
|
b0e3de1083 | |
|
08e8725ba5 | |
|
837c74d08e | |
|
6677c58cff | |
|
eff6458a2a | |
|
0bba44ee29 | |
|
76673a8ed8 | |
|
511c4763d3 | |
|
88312422d8 | |
|
6793be20ca | |
|
5c3405eceb | |
|
47addd943a | |
|
48af738ec4 | |
|
1534392c62 | |
|
fe83f3cfd1 | |
|
b3945fcd33 | |
|
11f9c1f23a | |
|
b4d89fc7c5 | |
|
7288277e87 | |
|
095fe62fa6 | |
|
a8d0d78913 | |
|
7d69bdab70 | |
|
55b10a43c4 | |
|
84eee5a69e | |
|
e45292de08 | |
|
ff5a3fa8bf | |
|
3f9448f5d2 | |
|
80d8fcd8b1 | |
|
5a7c0326ab | |
|
ce7194ff6a | |
|
75a8c6a309 | |
|
01df3705d1 | |
|
a391c83a2c | |
|
70b3bb6aae | |
|
5e0c73bec7 | |
|
c7d23d18d0 | |
|
4b705b127c | |
|
4d288813e2 | |
|
c0a778eeab | |
|
fd99c6dc6e | |
|
4c69781504 | |
|
928747f795 | |
|
81fc722b72 | |
|
0ec951ee11 | |
|
0b81425a13 | |
|
d3f63a8ecd | |
|
92499aa152 | |
|
caa36a21da | |
|
47ef38e9f1 | |
|
09e2208062 | |
|
bd5fd07d27 | |
|
9e0f624ad5 | |
|
422ec9ad4f | |
|
261ca1f092 | |
|
8684df62a6 | |
|
84322d07a9 | |
|
e513c0d355 | |
|
e834368502 | |
|
0cf0fa95ff | |
|
fda332d60b | |
|
3c2669ed7d | |
|
5f713f0319 | |
|
b2cbf99e73 | |
|
f9131aa18f | |
|
49330322ef | |
|
8f095520f4 | |
|
916d80c36d | |
|
f6e1df87be | |
|
cc276643a2 | |
|
a39cbebf94 | |
|
0f0b391f67 | |
|
65548b6fe0 | |
|
f0a6bd146b | |
|
35f61c4aa4 | |
|
eb85b3e3a6 | |
|
3348d501e8 | |
|
7609585dc3 | |
|
dbe25f282d | |
|
8fa7c94605 | |
|
8ebb734a31 | |
|
726c6978fc | |
|
2af2599a7d | |
|
708826cc49 | |
|
104b861747 | |
|
a391762771 | |
|
0ce3f4ead9 | |
|
39ff54d392 | |
|
a761e0d114 | |
|
f2e718639a | |
|
f1f535846e | |
|
60e16298f2 | |
|
f7036b2837 | |
|
abaf29f74d | |
|
c95a969af7 | |
|
6e90ec6dcc | |
|
22764a095a | |
|
b285737382 | |
|
b6a43dd65a | |
|
5eb81aa47f | |
|
64eddf5d26 | |
|
1ddac5bddb | |
|
248d57da4e | |
|
eb921ed9d5 | |
|
fcac276955 | |
|
242fd1bb56 | |
|
7c7bd56b2e | |
|
7eec7e4e34 | |
|
251798c711 | |
|
2b8b6719a7 | |
|
1e59918c11 | |
|
4d14c1473f | |
|
06284be673 | |
|
76b87ade93 | |
|
9717e512b4 | |
|
535b000195 | |
|
3e5c805418 | |
|
7d50f6130b | |
|
be7d97c885 | |
|
bc2689f58d | |
|
38d5b5b4fa | |
|
7ae64edf3d | |
|
bc16692d9b | |
|
2f263898c6 | |
|
9bd7f8d02f | |
|
da728b8d70 | |
|
3ac18e4e06 | |
|
b180f363ac | |
|
6f3675b13c | |
|
548379ca2a | |
|
3938c3755e | |
|
9af79cf51a | |
|
fa0039c6a8 | |
|
6846ef87b4 | |
|
5d762102fe | |
|
751fd7b245 | |
|
7a8213af43 | |
|
5d8c36eca0 | |
|
d2ee73b9d3 | |
|
e2d75e70d1 | |
|
26c795fdea | |
|
c479be7069 | |
|
77f11cda3b | |
|
ef3191d3e5 | |
|
2b4b44292a | |
|
dafb8a1ab7 | |
|
41c9a6d9b0 | |
|
5a66f9ad72 | |
|
dd30054138 | |
|
d31c43bc09 | |
|
eddb17689c | |
|
2bb77e60e5 | |
|
965655be3a | |
|
e49eb47e36 | |
|
63f8b3b323 | |
|
90163ba709 | |
|
4aa51e9956 | |
|
c9558026d0 | |
|
77a2c9ab3b | |
|
cc99cd8f0f | |
|
1ef38a6929 | |
|
c213352800 | |
|
f8479253fa | |
|
2ac5efa04a | |
|
2d76947c07 | |
|
3eb94c09c4 | |
|
5af4c42728 | |
|
01427b1755 | |
|
e095b77048 | |
|
f0c092da21 | |
|
c3e502032e | |
|
a5faee46bd | |
|
641eb13e5d | |
|
e56d520db4 | |
|
2c37cda1aa | |
|
faa57a8a2a | |
|
c2920c9f9b | |
|
5dc6a1cd13 | |
|
87e1e217c3 | |
|
310b9489d3 | |
|
8dd86c31a5 | |
|
4be2ea8afb | |
|
54143dfa37 | |
|
bbff92c13f | |
|
9d3e6cd8f5 | |
|
598effcabc | |
|
80f9364edc | |
|
1ec2b4d17c | |
|
25b1aa5abc | |
|
309b422425 | |
|
07f0310d34 | |
|
92595b4348 | |
|
943fed851e | |
|
cc60ef8202 | |
|
9189dba001 | |
|
b0b377af0c | |
|
00cf01f901 | |
|
f5de99b249 | |
|
6bd9fca080 | |
|
7a4c6ca2f9 | |
|
994ec98b48 | |
|
1d8603c491 | |
|
7373476fd8 | |
|
9e5897a25f | |
|
145ab8530f | |
|
a9051ef991 | |
|
b926f28c74 | |
|
e85e0f600b | |
|
c13f35ad49 | |
|
3c24dd9a00 | |
|
3a6ddf927d | |
|
99cea38fd0 | |
|
d213c8304a | |
|
ed21707cf0 | |
|
7423283f5d | |
|
6fe3ba9153 | |
|
6d4cb6c3ad | |
|
12c3e3aadb | |
|
0ce1fa9f87 | |
|
f18d3487c6 | |
|
94a602d17e | |
|
37e7f0c8ab | |
|
c897c24974 | |
|
542c6e885c | |
|
2ab4a26396 | |
|
d49806a78b | |
|
666f9c5d5b | |
|
0ba720df03 | |
|
b17af8d583 | |
|
e983f0c9e1 | |
|
65f6beb00b | |
|
1fa89d6fb9 | |
|
6c6df15286 | |
|
aebbcd2f15 | |
|
3128c9395c | |
|
e4d598cc3e | |
|
01b4c4a3a7 | |
|
b96d40c028 | |
|
4ccef202d3 | |
|
770838678d | |
|
80f3e93367 | |
|
0ca56309ea | |
|
1ebad655e4 | |
|
138b99c703 | |
|
03753b6fe2 | |
|
c813bf19fb | |
|
80f015c2d2 | |
|
b8739cf80f | |
|
9cd0108fc6 | |
|
2f7dd90f1f | |
|
b1a0cb8c3b | |
|
edf93fca17 | |
|
aec1179a30 | |
|
dcf83dc6af | |
|
5c566274c0 | |
|
1b7c346bca | |
|
25ead1ce97 | |
|
fc83ff14a5 | |
|
bf2679a885 | |
|
59b3ac22a8 | |
|
bebac1ee3f | |
|
03fc097f28 | |
|
b1b2d5df0c | |
|
ff9b25a905 | |
|
1fb7b0e24d | |
|
4c9e91423f | |
|
82a8e3d9a1 | |
|
6a1b55568c | |
|
b9129e6973 | |
|
5da8170d9e | |
|
b79763d065 | |
|
4a6f0a4aa0 | |
|
1f27ca57b2 | |
|
828a71e576 | |
|
3b45c76d0f | |
|
98174251f1 | |
|
a54064b989 | |
|
fba8cf9480 | |
|
1f8d34ffb9 | |
|
7eca34552d | |
|
3a10c77cbd | |
|
ba1901b2a0 | |
|
e791e9006c | |
|
0a056adee4 | |
|
3630bb9ae4 | |
|
1b625228b9 | |
|
0abfceebaa | |
|
825fa3171c | |
|
1fb4dd70e8 | |
|
d058244594 | |
|
a007c5a3fa | |
|
117152d090 | |
|
b34b6e0ad4 | |
|
492b644679 | |
|
5a0b0270f3 | |
|
790ccc3961 | |
|
54f67dc3b0 | |
|
22428cd58c | |
|
c41487a1d1 | |
|
a4be61377a | |
|
21cd515d4d | |
|
ef08458e8d | |
|
aa3f5fa4f4 | |
|
c07f46000d | |
|
4f335cb06f | |
|
cfd3cdf535 | |
|
45bf569ec4 | |
|
fed0396d9e | |
|
5aee1fe17e | |
|
52f8fb3416 | |
|
d375d61519 | |
|
79068d142b | |
|
e3ce44a286 | |
|
b39ee050f3 | |
|
87dd44dd59 | |
|
c80daae5a1 | |
|
84b55d8160 | |
|
6b43378396 | |
|
53fd8d79f8 | |
|
0e955b0f35 | |
|
6bbad5e9a9 | |
|
156b638518 | |
|
5d10c72664 | |
|
c3629633de | |
|
6342a0387f | |
|
6cfdcc0128 | |
|
5e80837424 | |
|
97f243f347 | |
|
8e97385a8f | |
|
f8046637ac | |
|
ef61c8618a | |
|
bf7ea4caed | |
|
f7fe187120 | |
|
238175b4bd | |
|
e9ff88f453 | |
|
ee254a1812 | |
|
a488a71dbd | |
|
bcc5b559a3 | |
|
67e8f3708a | |
|
3aef34e552 | |
|
3c19beacbb | |
|
f1d10b47ce | |
|
ddbb568154 | |
|
7010986643 | |
|
2f18a83006 | |
|
432a2e15db | |
|
85bec7fddb | |
|
1cdb431ae2 | |
|
64f64caaff | |
|
8865659a8a | |
|
9dc5430652 | |
|
7285e4834a | |
|
c1598584d8 | |
|
100a7ea91c | |
|
696e4cfa80 | |
|
14d966d159 | |
|
0f9100f185 | |
|
bff4ea7fd5 | |
|
ead1fb2965 | |
|
0e946b0cd4 | |
|
044f999529 | |
|
00fe8fd59e | |
|
befea83a76 | |
|
b7e2f2a649 | |
|
bb3812fbfb | |
|
6f88cf1930 | |
|
fc2f66c514 | |
|
21b08af7a3 | |
|
ef143dc5eb | |
|
6a962583b0 | |
|
57ead01624 | |
|
2e6fbadd9e | |
|
322fa48267 | |
|
1f995ec674 | |
|
957058e2d4 | |
|
cac575398d | |
|
5981197299 | |
|
0a408476ec | |
|
966d5daed7 | |
|
539b317ccb | |
|
8c93710963 | |
|
68b93e1074 | |
|
c99441247c | |
|
b3a7bc521d | |
|
77c4cd7b01 | |
|
2bfc769c4d | |
|
3c7e398f6f | |
|
f1ef519a5c | |
|
564bbfae12 | |
|
ba655ddb8a | |
|
aa917089b9 | |
|
cc3fb8e3f1 | |
|
eb2cb315e6 | |
|
6f8ecfdf5c | |
|
1f52d23d67 | |
|
55128f0c9b | |
|
edc1fd3d83 | |
|
248aaf0541 | |
|
a6c5802940 | |
|
1808bd3703 | |
|
46318d2094 | |
|
15209b87b9 | |
|
557a2e48d2 | |
|
33d0475d82 | |
|
7373e1ae31 | |
|
11c2a19ad2 | |
|
76c73d55d2 | |
|
9afaa9a66e | |
|
0cf863a483 | |
|
8b1100e3ed | |
|
22cf48bd21 | |
|
9541bb5f15 | |
|
ca406b6d24 | |
|
e8a977f55b | |
|
ad9934159b | |
|
4bd03a25f2 | |
|
c509a9f7fe | |
|
d809260799 | |
|
121afda79b | |
|
b781dae339 | |
|
733ad1a024 | |
|
023856cbe9 | |
|
44a5a7ad81 | |
|
ae07480f45 | |
|
cf7f05db8b | |
|
315c2774cb | |
|
6d7ae88a94 | |
|
b5d03b845f | |
|
0c76bd21b9 | |
|
5a7283386c | |
|
9a0d34a7a3 | |
|
22f2abf225 | |
|
188fb10c96 | |
|
736a4ad185 | |
|
0b93c5c6dd | |
|
b530ec2a65 | |
|
2377792c22 | |
|
6f2a075bee | |
|
2320df1227 | |
|
1e0564048b | |
|
454bf93aaf | |
|
66c54a9646 | |
|
cd94be2fb8 | |
|
79ec4211ab | |
|
f38709b7cb | |
|
d095cc1161 | |
|
0ad75bd93e | |
|
05f10c859e | |
|
b60e09756d | |
|
a9a4fa498d | |
|
2ca081216b | |
|
0d862890e0 | |
|
3baa61590e | |
|
d99dca6fc0 | |
|
c5e905f5c4 | |
|
4a4ba74114 | |
|
d45943130c | |
|
29c60c7b42 | |
|
1b60245298 | |
|
2d016fa090 | |
|
779cf6a370 | |
|
80d2264bc9 | |
|
64517b5b02 | |
|
e14c48719e | |
|
0f7a567f18 | |
|
0fea9ebef0 | |
|
dd92b8d7e8 | |
|
4f2c16709b | |
|
6c8e806bc8 | |
|
b95224bd83 | |
|
edb8fa393e | |
|
2ccd666449 | |
|
3ba7a5d1e8 | |
|
b8fb9cded3 | |
|
e391fd0f3e | |
|
93a739d612 | |
|
fab269687b | |
|
c8ade9e9d0 | |
|
c352dcff7d | |
|
401b7e4296 | |
|
242f261934 | |
|
1fb78422fa | |
|
72c27b040c | |
|
d908a52753 | |
|
0b312e02d5 | |
|
756e936c4b | |
|
839bb9a81e | |
|
54d0f5b53d | |
|
fc4ea432dd | |
|
a65311dc7c | |
|
9d99f9d2af | |
|
266e9f5a9a | |
|
5091f71416 | |
|
212e9b1f85 | |
|
d9a736a4a8 | |
|
b76a49aedd | |
|
011bfbb236 | |
|
662458dc2c | |
|
ec204dfa14 | |
|
f5e9b53289 | |
|
1ee04421c0 | |
|
a23076d3a8 | |
|
c6e5d385c7 | |
|
e42b1327aa | |
|
85625829e0 | |
|
e1b7c31e0f | |
|
f4d92cf131 | |
|
d70ce8aba5 | |
|
99a864e9c6 | |
|
b089b0d786 | |
|
d557fb0808 | |
|
9a1ef08495 | |
|
236bb2bb16 | |
|
0a8b44d269 | |
|
2cb0d042e4 | |
|
24f000d978 | |
|
06d4b90071 | |
|
5636c15903 | |
|
419a464d06 | |
|
be1d71f626 | |
|
8b835b1b5c | |
|
2ee331ef44 | |
|
ea13e95d9e | |
|
baa557116c | |
|
66f27b9b83 | |
|
4185605327 | |
|
373e038301 | |
|
fec7742a69 | |
|
7365e3200b | |
|
805db78e19 | |
|
6796fac7f1 | |
|
5bf18b0728 | |
|
32a6b68f1d | |
|
664a7dfcd4 | |
|
11ce971ad0 | |
|
a944244a7b | |
|
d2a20a1090 | |
|
a438c72cc5 | |
|
fc06d35994 | |
|
a39c0236bb | |
|
19dae302a2 | |
|
095745c93b | |
|
f102f6302b | |
|
d888d758c0 | |
|
b884138923 | |
|
68294babdb | |
|
581b8c61bf | |
|
96cd8ff56b | |
|
72200ce935 | |
|
2830062259 | |
|
f816e52ac5 | |
|
d818168c05 | |
|
6d3ea21c16 | |
|
4ce8f97b15 | |
|
c17a490708 | |
|
fc9b9806c8 | |
|
aec678d252 | |
|
aa6d28268b | |
|
df18af9ba0 | |
|
5b46484277 | |
|
b3907caad2 | |
|
3b653a192b | |
|
01ce7df60f | |
|
8b28cb3993 | |
|
56aeeebfb0 | |
|
37541dd178 | |
|
552f50bdf1 | |
|
48370fd6a5 | |
|
62649b3915 | |
|
e1a649ed58 | |
|
827f03a45c | |
|
1c20cc83aa | |
|
d7cf478b52 | |
|
b01df10eae | |
|
1e57d4f316 | |
|
954c0df0c6 | |
|
327c785347 | |
|
c4b2921977 | |
|
4978cd8089 | |
|
8e81185375 | |
|
baec1b5b77 | |
|
8935b978f3 | |
|
d181fd0671 | |
|
9ced20fdcc | |
|
83a2826a59 | |
|
f15b4e2c86 | |
|
e3b6b7891c | |
|
0b62dd94ea | |
|
c2a683bca4 | |
|
d02720252e | |
|
a5429dc026 | |
|
f2edbd91c3 | |
|
f2f9b4a6f8 | |
|
c3f62991c3 | |
|
799d803fa0 | |
|
4ce5b5d3a0 | |
|
f1ed1ee023 | |
|
865b782caf | |
|
f967773b2a | |
|
20d8047198 | |
|
bed8ca2566 | |
|
2143502a9d | |
|
8d4c577032 | |
|
a22ff61af2 | |
|
94c5c18541 | |
|
cca14a0230 | |
|
06cac9e7d4 | |
|
699e0587d1 | |
|
f7e2021e93 | |
|
9f0c10fdff | |
|
45e14af4a9 | |
|
6bc4441850 | |
|
8076be2db1 | |
|
fbe58f7cf8 | |
|
e0a68289c5 | |
|
73bb418719 | |
|
058d9d46c0 | |
|
8117b0403f | |
|
366e93f50b | |
|
7a8803cb38 | |
|
a55bf40ee9 | |
|
3daca1e960 | |
|
ea2266e453 | |
|
b039b51610 | |
|
8f2077e344 | |
|
293a22fd31 | |
|
6028826826 | |
|
cae44f0458 | |
|
8f347fbacb | |
|
0e4af6d453 | |
|
b7ba73f1b1 | |
|
6ec66a7e4a | |
|
82546fdf47 | |
|
5b025075dc | |
|
423db34bf3 | |
|
2b6ced06a1 | |
|
1f94f63bc1 | |
|
d9a3f6226a | |
|
795952c182 | |
|
376ac4649e | |
|
119a1bc6c4 | |
|
0e89a9c837 | |
|
3113333cb2 | |
|
ae7a5df47b | |
|
71e7e5b618 | |
|
dae0bf863e | |
|
1d39e41d00 | |
|
5be2b3f867 | |
|
2189b9a746 | |
|
8ad53a4c0e | |
|
89bf94245b | |
|
41d71c5a5b | |
|
ca0c3aaa47 | |
|
9bde8f859b | |
|
7247b6637e | |
|
b86615e2b7 | |
|
2ffc15a70c | |
|
c7f34c3452 | |
|
cd8be0e0a6 | |
|
11ef948854 | |
|
cd5f8b0ee7 | |
|
4e6351b856 | |
|
281f554365 | |
|
e9afb46eda | |
|
c9388fa955 | |
|
09f4c7d026 | |
|
616a81b496 | |
|
3435b43a5c | |
|
063983ccc9 | |
|
4bb284bb60 | |
|
1e8fe6a0d7 | |
|
85ed753609 | |
|
84da11f98e | |
|
e86ecab507 | |
|
daaf5e381a | |
|
e321bafdf9 | |
|
b7543e9402 | |
|
267d24de0a | |
|
27c6279d6c | |
|
39e04a1756 | |
|
fc1aefb8d4 | |
|
fea70e69c9 | |
|
a18c1e7e5e | |
|
c467b43c38 | |
|
d204bfa432 | |
|
049e888039 | |
|
5b6bffb5b3 | |
|
eed69a2573 | |
|
8b8d474b92 | |
|
e4918ba490 | |
|
0b1070af01 | |
|
fc27aa9561 | |
|
41ad561f11 | |
|
57afba8c51 | |
|
f1c437363c | |
|
6db59cba6f | |
|
024bff6a91 | |
|
3fadbe9c1d | |
|
28e07d9df3 | |
|
ac94c2b988 | |
|
a3ae44aca0 | |
|
9dcf30efe9 | |
|
dd4530cad6 | |
|
9a76fb4c07 | |
|
686ce5bae3 | |
|
2e0f496713 | |
|
5d81bfc58b | |
|
504675fc78 | |
|
3b9d3d0212 | |
|
2281e95940 | |
|
5dca27278a | |
|
ef232eae0a | |
|
49c2bc214a | |
|
0d9eebe3bb | |
|
54ca067610 | |
|
0648dc581f | |
|
63482ef061 | |
|
1e9affe4ba | |
|
550cfa2a86 | |
|
d49dfa0293 | |
|
4ebbd0e5c2 | |
|
c5f0be83a8 | |
|
bd0c75fe4b | |
|
b556d96148 | |
|
61047a028d | |
|
2cdc4ddcbf | |
|
ccf52f0822 | |
|
adaf7dee7c | |
|
ba7161b39e | |
|
a5150265ec | |
|
1771bfabcc | |
|
853ca03491 | |
|
b509c759c4 | |
|
5cf37aae5b | |
|
93c9f63b0d | |
|
bffa4b6ab6 | |
|
5d453b5038 | |
|
2e22583fcc | |
|
b63133d1d6 | |
|
edd2d301e5 | |
|
990307f3d5 | |
|
4319414fb2 | |
|
d3e2520b1f | |
|
e9899a93cf | |
|
fd9bef4aa7 | |
|
3028329126 | |
|
89e4555bc0 | |
|
dfda177ff7 |
|
@ -0,0 +1,2 @@
|
|||
coverage
|
||||
dist
|
|
@ -0,0 +1,14 @@
|
|||
{
|
||||
"extends": "loopback",
|
||||
"rules": {
|
||||
"max-len": ["error", 110, 4, {
|
||||
"ignoreComments": true,
|
||||
"ignoreUrls": true,
|
||||
"ignorePattern": "^\\s*var\\s.+=\\s*(require\\s*\\()|(/)"
|
||||
}],
|
||||
// NOTE(bajtos) we should eventually remove this override
|
||||
// and fix all of those 100+ violations
|
||||
"one-var": "off",
|
||||
"no-unused-expressions": "off"
|
||||
}
|
||||
}
|
|
@ -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.
|
|
@ -0,0 +1,17 @@
|
|||
<!--
|
||||
Please provide a high-level description of the changes made by your pull request.
|
||||
|
||||
Include references to all related GitHub issues and other pull requests, for example:
|
||||
|
||||
Fixes #123
|
||||
Implements #254
|
||||
See also #23
|
||||
-->
|
||||
|
||||
## 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](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]
|
|
@ -0,0 +1,24 @@
|
|||
# Number of days of inactivity before an issue becomes stale
|
||||
daysUntilStale: 60
|
||||
# Number of days of inactivity before a stale issue is closed
|
||||
daysUntilClose: 14
|
||||
# Issues with these labels will never be considered stale
|
||||
exemptLabels:
|
||||
- pinned
|
||||
- security
|
||||
- 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
|
||||
markComment: >
|
||||
This issue has been automatically marked as stale because it has not had
|
||||
recent activity. It will be closed if no further activity occurs. Thank you
|
||||
for your contributions.
|
||||
# Comment to post when closing a stale issue. Set to `false` to disable
|
||||
closeComment: >
|
||||
This issue has been closed due to continued inactivity. Thank you for your understanding.
|
||||
If you believe this to be in error, please contact one of the code owners,
|
||||
listed in the `CODEOWNERS` file at the top-level of this repository.
|
|
@ -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
|
||||
|
@ -12,3 +14,5 @@ docs/man
|
|||
npm-debug.log
|
||||
.project
|
||||
test/memory.json
|
||||
.nyc_output
|
||||
dist
|
||||
|
|
|
@ -1,3 +0,0 @@
|
|||
[submodule "support/nodeunit"]
|
||||
path = support/nodeunit
|
||||
url = https://github.com/caolan/nodeunit.git
|
|
@ -1,2 +0,0 @@
|
|||
node_modules
|
||||
support/nodeunit
|
|
@ -0,0 +1 @@
|
|||
reporter: dot
|
|
@ -3,9 +3,13 @@ doc
|
|||
coverage.html
|
||||
coverage
|
||||
v8.log
|
||||
|
||||
.git
|
||||
.DS_Store
|
||||
benchmark.js
|
||||
analyse.r
|
||||
docs/html
|
||||
npm-debug.log
|
||||
.travis.yml
|
||||
.nyc_output
|
||||
dist
|
||||
types/__test__.ts
|
||||
|
|
|
@ -0,0 +1,9 @@
|
|||
{
|
||||
"exclude": [
|
||||
"test/**/*.js"
|
||||
],
|
||||
"reporter": [
|
||||
"html"
|
||||
],
|
||||
"cache": true
|
||||
}
|
|
@ -0,0 +1,2 @@
|
|||
*.json
|
||||
*.md
|
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"bracketSpacing": false,
|
||||
"singleQuote": true,
|
||||
"printWidth": 80,
|
||||
"trailingComma": "all"
|
||||
}
|
28
.travis.yml
28
.travis.yml
|
@ -1,6 +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:
|
||||
- "0.10"
|
||||
- "0.12"
|
||||
- "iojs"
|
||||
- 10.24.1
|
||||
- 12.22.12
|
||||
- 14.21.3
|
||||
- 16.20.2
|
||||
- 17.9.1
|
||||
|
|
1901
CHANGES.md
1901
CHANGES.md
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +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 precedence.
|
||||
|
||||
# Current maintainers
|
||||
* @jannyHou @dhmlau @zbarbuto @nitro404 @emonddr
|
||||
|
||||
# Alumni
|
||||
#
|
||||
# @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.
|
|
@ -11,9 +11,21 @@ Contributing to `loopback-datasource-juggler` is easy. In a few simple steps:
|
|||
|
||||
* Make something better or fix a bug.
|
||||
|
||||
* If possible update existing or add a new unit test in the [test](tests/) directory
|
||||
|
||||
* Adhere to code style outlined in the [Google C++ Style Guide][] and
|
||||
[Google Javascript Style Guide][].
|
||||
|
||||
* Run lint to check adherence the style guide. Note this is also run at the end of the tests.
|
||||
```
|
||||
npm run lint
|
||||
```
|
||||
|
||||
* Run tests and verify that they pass, or any that fail already failed before you introduced your change.
|
||||
```
|
||||
npm run test
|
||||
```
|
||||
|
||||
* Sign the [Contributor License Agreement](https://cla.strongloop.com/agreements/strongloop/loopback-datasource-juggler)
|
||||
|
||||
* Submit a pull request through Github.
|
||||
|
@ -147,5 +159,5 @@ Contributing to `loopback-datasource-juggler` is easy. In a few simple steps:
|
|||
inaccurate in any respect. Email us at callback@strongloop.com.
|
||||
```
|
||||
|
||||
[Google C++ Style Guide]: https://google-styleguide.googlecode.com/svn/trunk/cppguide.xml
|
||||
[Google Javascript Style Guide]: https://google-styleguide.googlecode.com/svn/trunk/javascriptguide.xml
|
||||
[Google C++ Style Guide]: https://google.github.io/styleguide/cppguide.html
|
||||
[Google Javascript Style Guide]: https://google.github.io/styleguide/javascriptguide.xml
|
||||
|
|
|
@ -0,0 +1,25 @@
|
|||
Copyright (c) IBM Corp. 2011,2017. All Rights Reserved.
|
||||
Node module: loopback-datasource-juggler
|
||||
This project is licensed under the MIT License, full text below.
|
||||
|
||||
--------
|
||||
|
||||
MIT license
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
|
@ -1,9 +0,0 @@
|
|||
Copyright (c) 2013-2015 StrongLoop, Inc.
|
||||
|
||||
loopback-datasource-juggler uses a dual license model.
|
||||
|
||||
You may use this library under the terms of the [MIT License][],
|
||||
or under the terms of the [StrongLoop Subscription Agreement][].
|
||||
|
||||
[MIT License]: http://opensource.org/licenses/MIT
|
||||
[StrongLoop Subscription Agreement]: http://strongloop.com/license
|
43
Makefile
43
Makefile
|
@ -1,43 +0,0 @@
|
|||
TESTER = ./node_modules/.bin/mocha
|
||||
OPTS = --growl
|
||||
TESTS = test/*.test.js
|
||||
|
||||
default: help
|
||||
|
||||
.PHONY: clean
|
||||
clean:
|
||||
rm -rf $(CURDIR)/node_modules
|
||||
|
||||
.PHONY: help
|
||||
help:
|
||||
@echo 'Usage: make [target]'
|
||||
@echo 'Targets:'
|
||||
@echo ' clean Delete `node_modules`'
|
||||
@echo ' help Print help (this message)'
|
||||
@echo ' refresh Delete `node_modules` and run `npm install`'
|
||||
@echo ' test Run tests in silent mode'
|
||||
@echo ' test-verbose Run tests in verbose mode'
|
||||
@echo ' testing Run tests continuously'
|
||||
|
||||
.PHONY: refresh
|
||||
refresh: clean
|
||||
npm install
|
||||
|
||||
.PHONY: test
|
||||
test:
|
||||
NO_DEPRECATION=loopback-datasource-juggler $(TESTER) $(OPTS) $(TESTS)
|
||||
|
||||
.PHONY: test-verbose
|
||||
test-verbose:
|
||||
$(TESTER) $(OPTS) --reporter spec $(TESTS)
|
||||
|
||||
.PHONY: testing
|
||||
testing:
|
||||
$(TESTER) $(OPTS) --watch $(TESTS)
|
||||
|
||||
# Deprecated targets
|
||||
|
||||
.PHONY: about-testing
|
||||
about-testing:
|
||||
@echo 'DEPRECATED: Use `make help` instead'
|
||||
make help
|
54
README.md
54
README.md
|
@ -1,22 +1,50 @@
|
|||
# LoopBack DataSource Juggler
|
||||
# loopback-datasource-juggler
|
||||
|
||||
LoopBack DataSource Juggler is an ORM that provides a common set of interfaces
|
||||
for interacting with databases, REST APIs, and other data sources. It was
|
||||
initially forked from [JugglingDB](https://github.com/1602/jugglingdb).
|
||||
An ORM/ODM that provides a common set of interfaces for interacting with databases, REST APIs, and other types of data sources. It was originally forked from [JugglingDB](https://github.com/1602/jugglingdb).
|
||||
|
||||
**For full documentation, see the official StrongLoop documentation**:
|
||||
[Connecting models to data sources](http://docs.strongloop.com/display/LB/Connecting+models+to+data+sources)
|
||||
|
||||
For information on creating data sources programmatically, see [Advanced topics: data sources](http://docs.strongloop.com/display/LB/Advanced+topics%3A+data+sources).
|
||||
## Supported versions
|
||||
|
||||
## Installation
|
||||
This module adopts the [Module Long Term Support (LTS)](http://github.com/CloudNativeJS/ModuleLTS) policy, with the following End Of Life (EOL) dates:
|
||||
|
||||
npm install loopback-datasource-juggler
|
||||
| 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 |
|
||||
|
||||
Also install the appropriated connector, for example for mongodb:
|
||||
Learn more about our LTS plan in the [LoopBack documentation](http://loopback.io/doc/en/contrib/Long-term-support.html).
|
||||
|
||||
npm install loopback-connector-mongodb
|
||||
## Usage
|
||||
|
||||
See [StrongLoop Documentation](http://docs.strongloop.com/) for more information.
|
||||
Install Juggler:
|
||||
|
||||
```
|
||||
npm install loopback-datasource-juggler
|
||||
```
|
||||
|
||||
Then install a connector:
|
||||
|
||||
```
|
||||
npm install loopback-connector-mongodb // in this case, the mongodb connector
|
||||
```
|
||||
|
||||
## Documentation
|
||||
|
||||
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,13 @@
|
|||
{
|
||||
"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",
|
||||
"lib/include.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,46 +1,53 @@
|
|||
var DataSource = require('../../loopback-datasource-juggler').DataSource;
|
||||
var ModelBuilder = require('../../loopback-datasource-juggler').ModelBuilder;
|
||||
var introspectType = require('../lib/introspection')(ModelBuilder);
|
||||
// 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
|
||||
|
||||
var ds = new DataSource('memory');
|
||||
'use strict';
|
||||
|
||||
const DataSource = require('../../loopback-datasource-juggler').DataSource;
|
||||
const ModelBuilder = require('../../loopback-datasource-juggler').ModelBuilder;
|
||||
const introspectType = require('../lib/introspection')(ModelBuilder);
|
||||
|
||||
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',
|
||||
pushSettings: [
|
||||
{ "platform": "apns",
|
||||
"apns": {
|
||||
"pushOptions": {
|
||||
"gateway": "gateway.sandbox.push.apple.com",
|
||||
"cert": "credentials/apns_cert_dev.pem",
|
||||
"key": "credentials/apns_key_dev.pem"
|
||||
{'platform': 'apns',
|
||||
'apns': {
|
||||
'pushOptions': {
|
||||
'gateway': 'gateway.sandbox.push.apple.com',
|
||||
'cert': 'credentials/apns_cert_dev.pem',
|
||||
'key': 'credentials/apns_key_dev.pem',
|
||||
},
|
||||
|
||||
"feedbackOptions": {
|
||||
"gateway": "feedback.sandbox.push.apple.com",
|
||||
"cert": "credentials/apns_cert_dev.pem",
|
||||
"key": "credentials/apns_key_dev.pem",
|
||||
"batchFeedback": true,
|
||||
"interval": 300
|
||||
}
|
||||
}}
|
||||
]}
|
||||
'feedbackOptions': {
|
||||
'gateway': 'feedback.sandbox.push.apple.com',
|
||||
'cert': 'credentials/apns_cert_dev.pem',
|
||||
'key': 'credentials/apns_key_dev.pem',
|
||||
'batchFeedback': true,
|
||||
'interval': 300,
|
||||
},
|
||||
}},
|
||||
]};
|
||||
|
||||
console.log(new Application(application).toObject());
|
||||
|
||||
Application.create(application, function (err, app1) {
|
||||
Application.create(application, function(err, app1) {
|
||||
console.log('Created: ', app1.toObject());
|
||||
Application.findById(app1.id, function (err, app2) {
|
||||
Application.findById(app1.id, function(err, app2) {
|
||||
console.log('Found: ', app2.toObject());
|
||||
});
|
||||
});
|
||||
|
||||
// Instance JSON document
|
||||
var user = {
|
||||
const user = {
|
||||
name: 'Joe',
|
||||
age: 30,
|
||||
birthday: new Date(),
|
||||
|
@ -50,32 +57,30 @@ var user = {
|
|||
city: 'San Jose',
|
||||
state: 'CA',
|
||||
zipcode: '95131',
|
||||
country: 'US'
|
||||
country: 'US',
|
||||
},
|
||||
friends: ['John', 'Mary'],
|
||||
emails: [
|
||||
{label: 'work', id: 'x@sample.com'},
|
||||
{label: 'home', id: 'x@home.com'}
|
||||
{label: 'home', id: 'x@home.com'},
|
||||
],
|
||||
tags: []
|
||||
tags: [],
|
||||
};
|
||||
|
||||
// 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());
|
||||
|
||||
User.create(user, function (err, u1) {
|
||||
User.create(user, function(err, u1) {
|
||||
console.log('Created: ', u1.toObject());
|
||||
User.findById(u1.id, function (err, u2) {
|
||||
User.findById(u1.id, function(err, u2) {
|
||||
console.log('Found: ', u2.toObject());
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
|
|
|
@ -1,42 +1,45 @@
|
|||
var ModelBuilder = require('../../loopback-datasource-juggler').ModelBuilder;
|
||||
var modelBuilder = new ModelBuilder();
|
||||
// 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';
|
||||
|
||||
const ModelBuilder = require('../../loopback-datasource-juggler').ModelBuilder;
|
||||
const modelBuilder = new ModelBuilder();
|
||||
// define models
|
||||
var Post = modelBuilder.define('Post', {
|
||||
title: { type: String, length: 255 },
|
||||
content: { type: ModelBuilder.Text },
|
||||
date: { type: Date, default: function () {
|
||||
const Post = modelBuilder.define('Post', {
|
||||
title: {type: String, length: 255},
|
||||
content: {type: ModelBuilder.Text},
|
||||
date: {type: Date, default: function() {
|
||||
return new Date();
|
||||
} },
|
||||
timestamp: { type: Number, default: Date.now },
|
||||
published: { type: Boolean, default: false, index: true }
|
||||
}},
|
||||
timestamp: {type: Number, default: Date.now},
|
||||
published: {type: Boolean, default: false, index: true},
|
||||
});
|
||||
|
||||
// simpler way to describe model
|
||||
var User = modelBuilder.define('User', {
|
||||
const User = modelBuilder.define('User', {
|
||||
name: String,
|
||||
bio: ModelBuilder.Text,
|
||||
approved: Boolean,
|
||||
joinedAt: Date,
|
||||
age: Number
|
||||
age: Number,
|
||||
});
|
||||
|
||||
var Group = modelBuilder.define('Group', {group: String});
|
||||
const Group = modelBuilder.define('Group', {group: String});
|
||||
|
||||
// define any custom method
|
||||
User.prototype.getNameAndAge = function () {
|
||||
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);
|
||||
console.log(modelBuilder.definitions);
|
||||
|
||||
User.mixin(Group);
|
||||
var user = new User({name: 'Ray', group: 'Admin'});
|
||||
user = new User({name: 'Ray', group: 'Admin'});
|
||||
console.log(user);
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -1,35 +1,42 @@
|
|||
var DataSource = require('../../loopback-datasource-juggler').DataSource;
|
||||
var ModelBuilder = require('../../loopback-datasource-juggler').ModelBuilder;
|
||||
var ds = new DataSource('memory');
|
||||
// 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';
|
||||
|
||||
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', {
|
||||
title: { type: String, length: 255 },
|
||||
content: { type: DataSource.Text },
|
||||
date: { type: Date, default: function () {
|
||||
const Post = ds.define('Post', {
|
||||
title: {type: String, length: 255},
|
||||
content: {type: DataSource.Text},
|
||||
date: {type: Date, default: function() {
|
||||
return new Date;
|
||||
} },
|
||||
timestamp: { type: Number, default: Date.now },
|
||||
published: { type: Boolean, default: false, index: true }
|
||||
}},
|
||||
timestamp: {type: Number, default: Date.now},
|
||||
published: {type: Boolean, default: false, index: true},
|
||||
});
|
||||
|
||||
// simplier way to describe model
|
||||
var User = ds.define('User', {
|
||||
const User = ds.define('User', {
|
||||
name: String,
|
||||
bio: DataSource.Text,
|
||||
approved: Boolean,
|
||||
joinedAt: Date,
|
||||
age: Number
|
||||
age: Number,
|
||||
});
|
||||
|
||||
var Group = ds.define('Group', {name: String});
|
||||
const Group = ds.define('Group', {name: String});
|
||||
|
||||
// define any custom method
|
||||
User.prototype.getNameAndAge = function () {
|
||||
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);
|
||||
|
@ -51,46 +58,46 @@ Post.belongsTo(User, {as: 'author', foreignKey: 'userId'});
|
|||
|
||||
User.hasAndBelongsToMany('groups');
|
||||
|
||||
var user2 = new User({name: 'Smith', age: 14});
|
||||
user2.save(function (err) {
|
||||
const user2 = new User({name: 'Smith', age: 14});
|
||||
user2.save(function(err) {
|
||||
console.log(user2);
|
||||
var post = user2.posts.build({title: 'Hello world'});
|
||||
post.save(function (err, data) {
|
||||
const post = user2.posts.build({title: 'Hello world'});
|
||||
post.save(function(err, data) {
|
||||
console.log(err ? err : data);
|
||||
});
|
||||
});
|
||||
|
||||
Post.findOne({where: {published: false}, order: 'date DESC'}, function (err, data) {
|
||||
Post.findOne({where: {published: false}, order: 'date DESC'}, function(err, data) {
|
||||
console.log(data);
|
||||
});
|
||||
|
||||
User.create({name: 'Jeff', age: 12}, function (err, data) {
|
||||
User.create({name: 'Jeff', age: 12}, function(err, data) {
|
||||
if (err) {
|
||||
console.log(err);
|
||||
return;
|
||||
}
|
||||
console.log(data);
|
||||
var post = data.posts.build({title: 'My Post'});
|
||||
const post = data.posts.build({title: 'My Post'});
|
||||
console.log(post);
|
||||
});
|
||||
|
||||
User.create({name: 'Ray'}, function (err, data) {
|
||||
User.create({name: 'Ray'}, function(err, data) {
|
||||
console.log(data);
|
||||
});
|
||||
|
||||
User.scope('minors', {where: {age: {lte: 16}}, include: 'posts'});
|
||||
User.minors(function (err, kids) {
|
||||
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) {
|
||||
article.tags.create({name: 'popular'}, function (err, data) {
|
||||
Article.findOne(function (e, article) {
|
||||
article.tags(function (e, tags) {
|
||||
Article.create(function(e, article) {
|
||||
article.tags.create({name: 'popular'}, function(err, data) {
|
||||
Article.findOne(function(e, article) {
|
||||
article.tags(function(e, tags) {
|
||||
console.log(tags);
|
||||
});
|
||||
});
|
||||
|
@ -98,10 +105,10 @@ 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();
|
||||
|
||||
Color = modelBuilder.define('Color', {
|
||||
name: String
|
||||
const Color = modelBuilder.define('Color', {
|
||||
name: String,
|
||||
});
|
||||
|
||||
// attach
|
||||
|
@ -111,7 +118,7 @@ Color.create({name: 'red'});
|
|||
Color.create({name: 'green'});
|
||||
Color.create({name: 'blue'});
|
||||
|
||||
Color.all(function (err, colors) {
|
||||
Color.all(function(err, colors) {
|
||||
console.log(colors);
|
||||
});
|
||||
|
||||
|
|
|
@ -1,48 +1,53 @@
|
|||
var jdb = require('../index');
|
||||
// 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
|
||||
|
||||
var User, Post, Passport, City, Street, Building;
|
||||
var nbSchemaRequests = 0;
|
||||
'use strict';
|
||||
|
||||
setup(function () {
|
||||
const jdb = require('../index');
|
||||
|
||||
Passport.find({include: 'owner'}, function (err, passports) {
|
||||
let User, Post, Passport, City, Street, Building;
|
||||
const nbSchemaRequests = 0;
|
||||
|
||||
setup(function() {
|
||||
Passport.find({include: 'owner'}, function(err, passports) {
|
||||
console.log('passports.owner', passports);
|
||||
});
|
||||
|
||||
User.find({include: 'posts'}, function (err, users) {
|
||||
User.find({include: 'posts'}, function(err, users) {
|
||||
console.log('users.posts', users);
|
||||
});
|
||||
|
||||
Passport.find({include: {owner: 'posts'}}, function (err, passports) {
|
||||
Passport.find({include: {owner: 'posts'}}, function(err, passports) {
|
||||
console.log('passports.owner.posts', passports);
|
||||
});
|
||||
|
||||
Passport.find({
|
||||
include: {owner: {posts: 'author'}}
|
||||
}, function (err, passports) {
|
||||
include: {owner: {posts: 'author'}},
|
||||
}, function(err, passports) {
|
||||
console.log('passports.owner.posts.author', passports);
|
||||
});
|
||||
|
||||
User.find({include: ['posts', 'passports']}, function (err, users) {
|
||||
User.find({include: ['posts', 'passports']}, function(err, users) {
|
||||
console.log('users.passports && users.posts', users);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
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');
|
||||
User = db.define('User', {
|
||||
name: String,
|
||||
age: Number
|
||||
age: Number,
|
||||
});
|
||||
Passport = db.define('Passport', {
|
||||
number: String
|
||||
number: String,
|
||||
});
|
||||
Post = db.define('Post', {
|
||||
title: String
|
||||
title: String,
|
||||
});
|
||||
|
||||
Passport.belongsTo('owner', {model: User});
|
||||
|
@ -50,10 +55,10 @@ function setup(done) {
|
|||
User.hasMany('posts', {foreignKey: 'userId'});
|
||||
Post.belongsTo('author', {model: User, foreignKey: 'userId'});
|
||||
|
||||
db.automigrate(function () {
|
||||
var createdUsers = [];
|
||||
var createdPassports = [];
|
||||
var createdPosts = [];
|
||||
db.automigrate(function() {
|
||||
let createdUsers = [];
|
||||
let createdPassports = [];
|
||||
let createdPosts = [];
|
||||
createUsers();
|
||||
function createUsers() {
|
||||
clearAndCreate(
|
||||
|
@ -63,12 +68,12 @@ function setup(done) {
|
|||
{name: 'User B', age: 22},
|
||||
{name: 'User C', age: 23},
|
||||
{name: 'User D', age: 24},
|
||||
{name: 'User E', age: 25}
|
||||
{name: 'User E', age: 25},
|
||||
],
|
||||
function (items) {
|
||||
function(items) {
|
||||
createdUsers = items;
|
||||
createPassports();
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -78,12 +83,12 @@ function setup(done) {
|
|||
[
|
||||
{number: '1', ownerId: createdUsers[0].id},
|
||||
{number: '2', ownerId: createdUsers[1].id},
|
||||
{number: '3'}
|
||||
{number: '3'},
|
||||
],
|
||||
function (items) {
|
||||
function(items) {
|
||||
createdPassports = items;
|
||||
createPosts();
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -95,25 +100,24 @@ function setup(done) {
|
|||
{title: 'Post B', userId: createdUsers[0].id},
|
||||
{title: 'Post C', userId: createdUsers[0].id},
|
||||
{title: 'Post D', userId: createdUsers[1].id},
|
||||
{title: 'Post E'}
|
||||
{title: 'Post E'},
|
||||
],
|
||||
function (items) {
|
||||
function(items) {
|
||||
createdPosts = items;
|
||||
done();
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
function clearAndCreate(model, data, callback) {
|
||||
var createdItems = [];
|
||||
model.destroyAll(function () {
|
||||
const createdItems = [];
|
||||
model.destroyAll(function() {
|
||||
nextItem(null, null);
|
||||
});
|
||||
|
||||
var itemIndex = 0;
|
||||
let itemIndex = 0;
|
||||
|
||||
function nextItem(err, lastItem) {
|
||||
if (lastItem !== null) {
|
||||
|
|
|
@ -1,4 +1,11 @@
|
|||
var path = require('path'),
|
||||
// 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';
|
||||
|
||||
const path = require('path'),
|
||||
fs = require('fs'),
|
||||
DataSource = require('../lib/datasource').DataSource;
|
||||
|
||||
|
@ -14,21 +21,20 @@ 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 (var s in models) {
|
||||
var m = models[s];
|
||||
for (const s in models) {
|
||||
const m = models[s];
|
||||
console.log(m.modelName, new m());
|
||||
}
|
||||
|
||||
models = loadSchemasSync(path.join(__dirname, 'schemas.json'));
|
||||
for (var s in models) {
|
||||
var m = models[s];
|
||||
for (const s in models) {
|
||||
const m = models[s];
|
||||
console.log(m.modelName, new m());
|
||||
}
|
||||
|
|
|
@ -1,8 +1,15 @@
|
|||
var ModelBuilder = require('../../loopback-datasource-juggler').ModelBuilder;
|
||||
var modelBuilder = new ModelBuilder();
|
||||
// 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';
|
||||
|
||||
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,
|
||||
|
@ -13,21 +20,25 @@ var User = modelBuilder.define('User', {
|
|||
city: String,
|
||||
state: String,
|
||||
zipCode: String,
|
||||
country: String
|
||||
country: String,
|
||||
},
|
||||
emails: [
|
||||
{
|
||||
label: String,
|
||||
email: String
|
||||
}
|
||||
email: String,
|
||||
},
|
||||
],
|
||||
friends: [String]
|
||||
friends: [String],
|
||||
});
|
||||
|
||||
var user = new User({name: 'Joe', age: 20, address: {street: '123 Main St', 'city': 'San Jose', state: 'CA'},
|
||||
const user = new User({
|
||||
name: 'Joe',
|
||||
age: 20,
|
||||
address: {street: '123 Main St', 'city': 'San Jose', state: 'CA'},
|
||||
emails: [
|
||||
{label: 'work', email: 'xyz@sample.com'}
|
||||
{label: 'work', email: 'xyz@sample.com'},
|
||||
],
|
||||
friends: ['John', 'Mary']});
|
||||
friends: ['John', 'Mary'],
|
||||
});
|
||||
console.log(user);
|
||||
console.log(user.toObject());
|
||||
|
|
|
@ -1,34 +1,40 @@
|
|||
var DataSource = require('../index').DataSource;
|
||||
var ds = new DataSource('memory');
|
||||
// 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
|
||||
|
||||
var Order = ds.createModel('Order', {
|
||||
'use strict';
|
||||
|
||||
const DataSource = require('../index').DataSource;
|
||||
const ds = new DataSource('memory');
|
||||
|
||||
const Order = ds.createModel('Order', {
|
||||
items: [String],
|
||||
orderDate: Date,
|
||||
qty: Number
|
||||
qty: Number,
|
||||
});
|
||||
|
||||
var Customer = ds.createModel('Customer', {
|
||||
name: String
|
||||
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) {
|
||||
Customer.create({name: 'John'}, function(err, customer) {
|
||||
Order.create({customerId: customer.id, orderDate: new Date(), items: ['Book']}, function(err, order) {
|
||||
order1 = order;
|
||||
order.customer(console.log);
|
||||
order.customer(true, console.log);
|
||||
|
||||
Customer.create({name: 'Mary'}, function (err, customer2) {
|
||||
Customer.create({name: 'Mary'}, function(err, customer2) {
|
||||
order.customer(customer2);
|
||||
order.customer(console.log);
|
||||
});
|
||||
});
|
||||
|
||||
Order.create({orderDate: new Date(), items: ['Phone']}, function (err, order) {
|
||||
|
||||
Order.create({orderDate: new Date(), items: ['Phone']}, function(err, order) {
|
||||
order.customer.create({name: 'Smith'}, function(err, customer2) {
|
||||
console.log(order, customer2);
|
||||
order.save(function(err, order) {
|
||||
|
@ -36,20 +42,20 @@ 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);
|
||||
});
|
||||
});
|
||||
|
||||
Customer.hasMany(Order, {as: 'orders', foreignKey: 'customerId'});
|
||||
|
||||
Customer.create({name: 'Ray'}, function (err, customer) {
|
||||
Order.create({customerId: customer.id, qty: 3, orderDate: new Date()}, function (err, order) {
|
||||
Customer.create({name: 'Ray'}, function(err, customer) {
|
||||
Order.create({customerId: customer.id, qty: 3, orderDate: new Date()}, function(err, order) {
|
||||
order3 = order;
|
||||
customer.orders(console.log);
|
||||
customer.orders.create({orderDate: new Date(), qty: 4}, function (err, order) {
|
||||
customer.orders.create({orderDate: new Date(), qty: 4}, function(err, order) {
|
||||
console.log(order);
|
||||
Customer.include([customer], 'orders', function (err, results) {
|
||||
Customer.include([customer], 'orders', function(err, results) {
|
||||
console.log('Results: ', results);
|
||||
});
|
||||
customer.orders({where: {qty: 4}}, function(err, results) {
|
||||
|
@ -61,18 +67,18 @@ Customer.create({name: 'Ray'}, function (err, customer) {
|
|||
});
|
||||
});
|
||||
|
||||
var Physician = ds.createModel('Physician', {
|
||||
name: String
|
||||
const Physician = ds.createModel('Physician', {
|
||||
name: String,
|
||||
});
|
||||
|
||||
var Patient = ds.createModel('Patient', {
|
||||
name: String
|
||||
const Patient = ds.createModel('Patient', {
|
||||
name: String,
|
||||
});
|
||||
|
||||
var Appointment = ds.createModel('Appointment', {
|
||||
const Appointment = ds.createModel('Appointment', {
|
||||
physicianId: Number,
|
||||
patientId: Number,
|
||||
appointmentDate: Date
|
||||
appointmentDate: Date,
|
||||
});
|
||||
|
||||
Appointment.belongsTo(Patient);
|
||||
|
@ -81,55 +87,57 @@ Appointment.belongsTo(Physician);
|
|||
Physician.hasMany(Patient, {through: Appointment});
|
||||
Patient.hasMany(Physician, {through: Appointment});
|
||||
|
||||
Physician.create({name: 'Dr John'}, function (err, physician1) {
|
||||
Physician.create({name: 'Dr Smith'}, function (err, physician2) {
|
||||
Patient.create({name: 'Mary'}, function (err, patient1) {
|
||||
Patient.create({name: 'Ben'}, function (err, patient2) {
|
||||
Appointment.create({appointmentDate: new Date(), physicianId: physician1.id, patientId: patient1.id},
|
||||
function (err, appt1) {
|
||||
Appointment.create({appointmentDate: new Date(), physicianId: physician1.id, patientId: patient2.id},
|
||||
function (err, appt2) {
|
||||
Physician.create({name: 'Dr John'}, function(err, physician1) {
|
||||
Physician.create({name: 'Dr Smith'}, function(err, physician2) {
|
||||
Patient.create({name: 'Mary'}, function(err, patient1) {
|
||||
Patient.create({name: 'Ben'}, function(err, patient2) {
|
||||
Appointment.create(
|
||||
{appointmentDate: new Date(), physicianId: physician1.id, patientId: patient1.id},
|
||||
function(err, appt1) {
|
||||
Appointment.create(
|
||||
{appointmentDate: new Date(), physicianId: physician1.id, patientId: patient2.id},
|
||||
function(err, appt2) {
|
||||
physician1.patients(console.log);
|
||||
physician1.patients({where: {name: 'Mary'}}, console.log);
|
||||
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', {
|
||||
name: String
|
||||
const Assembly = ds.createModel('Assembly', {
|
||||
name: String,
|
||||
});
|
||||
|
||||
var Part = ds.createModel('Part', {
|
||||
partNumber: String
|
||||
const Part = ds.createModel('Part', {
|
||||
partNumber: String,
|
||||
});
|
||||
|
||||
Assembly.hasAndBelongsToMany(Part);
|
||||
Part.hasAndBelongsToMany(Assembly);
|
||||
|
||||
Assembly.create({name: 'car'}, function (err, assembly) {
|
||||
Part.create({partNumber: 'engine'}, function (err, part) {
|
||||
assembly.parts.add(part, function (err) {
|
||||
Assembly.create({name: 'car'}, function(err, assembly) {
|
||||
Part.create({partNumber: 'engine'}, function(err, part) {
|
||||
assembly.parts.add(part, function(err) {
|
||||
assembly.parts(function(err, parts) {
|
||||
console.log('Parts: ', parts);
|
||||
});
|
||||
|
||||
// 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?
|
||||
|
@ -141,7 +149,6 @@ Assembly.create({name: 'car'}, function (err, assembly) {
|
|||
});
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
});
|
||||
|
||||
|
|
|
@ -0,0 +1,36 @@
|
|||
// Copyright IBM Corp. 2018. 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
|
||||
|
||||
// Type definitions for loopback-datasource-juggler 3.x
|
||||
// Project: https://github.com/strongloop/loopback-datasource-juggler
|
||||
// Definitions by: Raymond Feng <https://github.com/raymondfeng>
|
||||
// TypeScript Version: 2.8
|
||||
|
||||
/**
|
||||
* Experimental TypeScript definitions to capture types of the key artifacts
|
||||
* from `loopback-datasource-juggler` module. One of the main purposes is to
|
||||
* leverage such types in `LoopBack 4`'s bridge to juggler.
|
||||
*
|
||||
* Please note some of the classes, properties, methods, and functions are
|
||||
* intentionally not included in the definitions because of one of the following
|
||||
* factors:
|
||||
*
|
||||
* - They are internal
|
||||
* - They are to be deprecated
|
||||
*/
|
||||
export * from './types/common';
|
||||
export * from './types/model';
|
||||
export * from './types/relation';
|
||||
export * from './types/query';
|
||||
export * from './types/datasource';
|
||||
export * from './types/kv-model';
|
||||
export * from './types/persisted-model';
|
||||
export * from './types/scope';
|
||||
export * from './types/transaction-mixin';
|
||||
export * from './types/relation-mixin';
|
||||
export * from './types/observer-mixin';
|
||||
export * from './types/validation-mixin';
|
||||
export * from './types/inclusion-mixin';
|
||||
export * from './types/connector';
|
18
index.js
18
index.js
|
@ -1,3 +1,13 @@
|
|||
// 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';
|
||||
|
||||
const SG = require('strong-globalize');
|
||||
SG.SetRootDir(__dirname);
|
||||
|
||||
exports.ModelBuilder = exports.LDL = require('./lib/model-builder.js').ModelBuilder;
|
||||
exports.DataSource = exports.Schema = require('./lib/datasource.js').DataSource;
|
||||
exports.ModelBaseClass = require('./lib/model.js');
|
||||
|
@ -5,12 +15,14 @@ exports.GeoPoint = require('./lib/geo.js').GeoPoint;
|
|||
exports.ValidationError = require('./lib/validations.js').ValidationError;
|
||||
|
||||
Object.defineProperty(exports, 'version', {
|
||||
get: function() {return require('./package.json').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);}
|
||||
get: function() { return require(commonTest); },
|
||||
});
|
||||
|
||||
exports.Transaction = require('loopback-connector').Transaction;
|
||||
|
||||
exports.KeyValueAccessObject = require('./lib/kvao');
|
||||
|
|
|
@ -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": "{{polymorphic}} {0} Beziehung: {1} erwartet keinen Parameter 'polymorphic.as' beim Definieren eines angepassten 'foreignKey'/'discriminator' ",
|
||||
"09483e03b91c8bd58732a74b3ef1ec13": "Ungültiges Datum: {0}",
|
||||
"0a5aa17f7866a85e3aee37ef5369403c": "LinkManyToMany hat ein Ziel empfangen, dass das erforderliche \"{0}\" nicht enthält",
|
||||
"0b16d3ffc42f91b4b9a4b3b50c41c838": "Die Reihenfolge {0} ist nicht gültig",
|
||||
"0bd753a8944ad0af85a939bb25273887": "Unbekannter Schlüssel {0} darf nicht ablaufen",
|
||||
"0c0b867aca0973ba26e887d3337cc4ec": "{{Polymorphic}}-Modell nicht gefunden: '{0}' nicht festgelegt",
|
||||
"0c4eb8b6c2ff6e51d7e195eee346ced9": "Tabelle '{0}' ist nicht vorhanden.",
|
||||
"0ff31abb394afb555df162e74ff1a0a0": "{{id}} kann nicht von {0} auf {1} aktualisiert werden, wenn für {{forceId}} true festgelegt ist",
|
||||
"1ae7d3e0be381efb32bfd1ba652f5172": "WARNUNG: {{polymorphic}} {0} Beziehung: {1} verwendet Schlüsselwort 'polymorphic.as', das in LoopBack.next VERALTET sein wird. Lesen Sie diese Dokumentation zu Ersatzlösungen (https://loopback.io/doc/en/lb3/Polymorphic-relations.html#deprecated-polymorphic-as)",
|
||||
"1daef4e937fe52136597ba8fd2060f55": "Verschachtelungstransaktionen werden nicht unterstützt",
|
||||
"21095484501dbff31af6556fa6039182": "Der {{offset/skip}}-Parameter {0} ist nicht gültig",
|
||||
"280f4550f90e133118955ec6f6f72830": "Diskriminatortyp {0} angegeben, aber es ist kein Modell mit diesem Namen vorhanden",
|
||||
"28697ec15968a7969211f6d035ba9260": "{{polymorphic}} {0} Beziehung: {1} erwartet keinen Parameter 'model'",
|
||||
"2c4904377a87fdab502118719cc0d266": "{{Transaction}} wird nicht unterstützt",
|
||||
"2c5c8519721f749aab13c2f04f41d611": "Die Eigenschaft {0} weist die ungültige Klausel {1} auf: Es wurden genau 2 Werte erwartet, aber {2} empfangen",
|
||||
"2f4af31c144bbfab1bbf479866acd820": "\nWARNUNG: {{LoopBack}}-Konnektor \"{0}\" ist als keines der folgenden Module installiert:\n\n {1}\n\nFühren Sie zur Behebung Folgendes aus:\n\n {{npm install {2} --save}}\n",
|
||||
"3864f9be10f27723074566d2b3893514": "Warnung: Modell {0}, {{strict mode: `throw`}} wurde entfernt. Verwenden Sie stattdessen {{`strict: true`}}, wodurch {{`Validation Error`}} für unbekannte Eigenschaften zurückgegeben wird.",
|
||||
"38dbf42c29a4645238cc3d632e88ebc9": "{{Relation.modelTo}} ist für Beziehung {0} nicht definiert und ist keine {{polymorphic}}",
|
||||
"3cde8cc9bca22c67278b202ab0720106": "Keine Instanz mit ID {0} für {1} gefunden",
|
||||
"416dfbb7b823f51c9f3800be81060b41": "Keine Instanz mit {{id}} {0} für {1} gefunden",
|
||||
"49b5afd8c6a19ad9c8abeffb2f8114eb": "BelongsTo-Methode \"getAsync()\" ist veraltet, verwenden Sie stattdessen \"get()\".",
|
||||
"4c78325cedbb826db3a05bf5df0e8546": "Sie müssen beim Ersetzen eine {{id}} angeben!",
|
||||
"4e31b1edd10dadb724d83387de0b5062": "Der {{limit}}-Parameter {0} ist nicht gültig",
|
||||
"514985b2327f061ffb1c932f6b909979": "Modell {0} ist nicht definiert.",
|
||||
"525c856e65daab43be247e7b5410febd": "{{polymorphic}} {0} Beziehung: {1} erwartet keinen Parameter 'polymorphic.selector' beim Definieren eines angepassten 'foreignKey'/'discriminator' ",
|
||||
"5c18ee111dd87540cdb19a2a93b33be9": "Transaktion wurde aufgrund eines Zeitlimits rückgängig gemacht",
|
||||
"5ec7e6664256f7ea78f4f06dafc7d974": "Transaktion ist nicht bereit, auf die Auflösung des zurückgegebenen Promise warten",
|
||||
"5ec8efeb715a2c34b440f2d76e2cf87d": " {0}",
|
||||
"6111399276924ffa3bc9a410cdfcb2e5": "Kein {{id}}-Name {0}",
|
||||
"614e3355647e4127c96256102dc63376": "Die Eigenschaft {0} weist die ungültige Klausel {1} auf: Es wurde eine Zeichenfolge oder RegExp erwartet",
|
||||
"62a2d80c405b7fec5f547c448ab1b6ff": "Die {{order}} {0} hat eine ungültige Richtung",
|
||||
"6502a117987610380b9068ef98b1b0ee": "Kein Datensatz gefunden in {0} für ({1}.{2} ,{3}.{4})",
|
||||
"67c2bf43b5281ab929617423ea8a6f3e": "Der Konnektor {0} unterstützt die Operation {{replaceById}} nicht. Hierbei handelt es sich nicht um einen Fehler in LoopBack. Wenden Sie sich an die Ersteller des Konnektors, vorzugsweise über GitHub-Probleme.",
|
||||
"6c3234937d69763fc7f6bcafccc59bbc": "{{Model::deleteById}} erfordert das Argument {{id}}",
|
||||
"6eb6fd4fbd73394000bc25f5776fd20c": "{{Model::exists}} erfordert das Argument {{id}}",
|
||||
"6fcc2ff0db7a4f490f5e0ce9e24691f3": "{{HasOne}}-Beziehung kann nicht mehr als eine Instanz von {0} erstellen",
|
||||
"728232e473bf80272c042df2b7e002f4": "{{polymorphic}} {0} Beziehung: {1} erfordert den Parameter 'polymorphic.discriminator', wenn der Parameter 'polymorphic.foreignKey' angegeben ist",
|
||||
"791ab3031a73ede03f7d6299a85e8289": "Zeitlimitüberschreitung bei Verbindungsherstellung nach {0} ms",
|
||||
"7b277018e43d41bc445731092b91547d": "Nicht verbunden",
|
||||
"7bbbdece4eea90e42aa5c0bce295e503": "{{Model::findById}} erfordert das Argument {{id}}",
|
||||
"7e9530c0399289be0ee601a604be71ff": "{{BelongsTo}}-Beziehung {0} ist leer",
|
||||
"7faa840eb6ce11250a141deb42a6c489": "Unbekannter {{scope}} für Beziehung: {0}",
|
||||
"8091838319a5cc7d6a34af2f2a616ce9": "Eigenschaftsname sollte nicht \"{{constructor}}\" sein in Modell: {0}",
|
||||
"881e4b0cb86ed59549248ee540a9fd10": "Eigenschaftsname \"{{constructor}}\" ist in {0}-Daten nicht zulässig",
|
||||
"89afd3a9249f5a8d3edda07d84ca049d": "{{Polymorphic}}-Modell nicht gefunden: '{0}'",
|
||||
"89bf6d92731fe7bd2146ce8d0bec205c": "Ungültiges Argument, muss eine Zeichenfolge, ein {{regex}}-Literalwert oder ein {{RegExp}}-Objekt sein",
|
||||
"8a39126103a157f501affa070367a1b0": "Die Instanz {0} ist nicht gültig. Details: {1}.",
|
||||
"8c5ab01638c1ac1d58168c6346a8481a": "Ungültige {{regex}}-Flags: {0}",
|
||||
"938401ea4ce48159efa9be1d4a5e8bab": "Elemente müssen ein Array sein: {0}",
|
||||
"9e1f143ee02946324d34da92f71bf74e": "{0} Beziehung: {1} erfordert den Parameter 'model'",
|
||||
"a004f310d315e592843776fab964eaeb": "{{Polymorphic}}-Beziehungen erfordern ein through-Modell",
|
||||
"a0cf0e09c26df14283223e84e6a10f00": "Attribute konnten nicht aktualisiert werden. {{Object}} mit {{id}} {0} ist nicht vorhanden!",
|
||||
"a2487abefef4259c2131d96cdb8543b1": "Verbindungsfehler: {0}\nNach der nächsten Anforderung findet ein Neuversuch statt.",
|
||||
"a25e41a39c60c4702e55d0c3936576a1": "Schlüssel stimmen nicht überein: {0}.{1}: {2}, {3}.{4}: {5}",
|
||||
"a327355560d495454fba2c1aad6bdf09": "Unbekannte Bereichsmethode: {0}",
|
||||
"a6c18a7f4390cd3d59a2a7a047ae2aab": "Führen Sie den Befehl \"{{npm install loopback-datasource-juggler}} {0}\" aus ",
|
||||
"a829dee089c912e68c18920ba015400c": "WARNUNG: Eigenschaft {{id}} darf für Modell {2} in Operationshook {{'loaded'}} nicht von {0} in {1} geändert werden",
|
||||
"a984a076c59e451948b2bcf7a393d860": "WARNUNG: Eigenschaft {{id}} darf für Modell {2} in Operationshook {{'before save'}} nicht von {0} in {1} geändert werden",
|
||||
"ac04cf275b71c1eb89a41cf6bbad7a64": "HasOne-Methode \"getAsync()\" ist veraltet, verwenden Sie stattdessen \"get()\".",
|
||||
"b138294f132edfe1eb2a8211150c7238": "Unerwartetes 'nicht definiert' in Abfrage",
|
||||
"b15b20280211ad258d92947f05b6e4a5": "Der Konnektor wurde nicht initialisiert.",
|
||||
"b278876ec93ef9760f00e83f38ba313d": "Scope-Methode \"getAsync()\" ist veraltet, verwenden Sie stattdessen \"get()\".",
|
||||
"ba0fd8106eb54de4d003a844206431fd": "Modellhook \"{0}\" ist veraltet, verwenden Sie stattdessen Operationshooks. {{http://docs.strongloop.com/display/LB/Operation+hooks}}",
|
||||
"baf2c8b0c5a574b8a894e9b6304fece1": "Bei der where-Klausel {0} handelt es sich nicht um ein {{object}}",
|
||||
"bdb11cc1c780c9ccac33c316cfdc9d82": "Typ nicht definiert für Eigenschaft {0}.{1}",
|
||||
"bdfb951c8ff7ce0cbc08c06f548fd927": "Wert ist ein leeres {{object}}",
|
||||
"bec226891a505828bfc76c5cfd73b336": "TTL für unbekannten Schlüssel {0} kann nicht abgerufen werden",
|
||||
"cd930369e86cdd222f7bd117c6f9fa94": "Unbekannter Standardwertprovider {0}",
|
||||
"cfee4d8149316d9a647c0885cf3cafaf": "Eigenschaftsnamen, die Punkt(e) enthalten, werden nicht unterstützt. Modell: {0}, dynamische Eigenschaft: {1}",
|
||||
"d40328eabd8756d795bcdd49d782d4e9": "DataSource unterstützt keine Transaktionen",
|
||||
"da02dd6c53d4148320eeb31718a7aebe": "Ungültiger Typ für Eigenschaft {0}",
|
||||
"da751a8a748adbde5b55fa83b707b4e2": "Eigenschaftsnamen, die Punkt(e) enthalten, werden nicht unterstützt. Modell: {0}, Eigenschaft: {1}",
|
||||
"db03083e9a768388fdbee865249ac67a": "Validierungsfehler in {{updateOrCreate()}} werden ignoriert:",
|
||||
"dd63416d9b7d9fa4181e89efd619dfd8": "Der Wert ist kein {{array}} oder {{object}} mit sequenziellen numerischen Indizes",
|
||||
"ddf0aa14803f1c84f4a97f3803f7471c": "Klassenname erforderlich",
|
||||
"e08ab0e1ab55f26c357061447b635905": "Keine Beziehung gefunden in {0} für ({1}.{2} ,{3}.{4})",
|
||||
"e0e9504e137a3c3339144b51ed76fef2": "Konnektor ist nicht ordnungsgemäß definiert: Er sollte ein {{connector}}-Member von dataSource erstellen",
|
||||
"e2f282cbe3efba001d6d3a09f7f6ca8c": "{{polymorphic}} {0} Beziehung: {1} erfordert den Parameter 'polymorphic.foreignKey', wenn der Parameter 'polymorphic.discriminator' angegeben ist",
|
||||
"e39e0f5d52bfbf511e645d19ecadd2fa": "Die Eigenschaft {0} weist die ungültige Klausel {1} auf: {2}",
|
||||
"e4434de4bb8f5a3cd1d416e4d80d7e0b": "Unbekannte \"{0}\" {{id}} \"{1}\".",
|
||||
"e54d944c2a2c85a23caa86027ae307cf": "Modelle, die nicht an diese Datenquelle angehängt sind, können nicht migriert werden: {0}",
|
||||
"e54f118a75e15e132f16b985274eb46d": "Beim Abfragefilter {0} handelt es sich nicht um ein {{object}}",
|
||||
"e55937649d8d7a11706b8cec22d02eae": "{{HasOne}}-Beziehung {0} ist leer",
|
||||
"e6161ae8459c79d810e2aa9d21282a39": "Sie müssen beim Aktualisieren von Attributen eine {{id}} angeben!",
|
||||
"eb56c2b0c30cf006e2df00a549ec9c2c": "Beziehung \"{0}\" ist nicht definiert für {1} modell",
|
||||
"ec42dca074f1818c447f7ad16e2d01af": "{0} wird vom zugeordneten Konnektor nicht angegeben",
|
||||
"ecb7aa804bf54c682999d20d6436104c": "Die {{transaction}} ist nicht aktiv: {0}",
|
||||
"f30809cb932b72a66416a709c8531530": "Der Konnektor unterstützt {{method}} innerhalb einer Transaktion nicht",
|
||||
"f41bd91dc0f000a79c0bf842f1b7fdf9": "konnte Liste nicht aus JSON-Zeichenfolge erstellen: {0}",
|
||||
"f6e8c96c93b9c7687d6c172b3695e898": "{{id}}-Eigenschaft ({0}) kann nicht von {1} in {2} aktualisiert werden",
|
||||
"fa9ae17e8e008d0eb0f0421a2972308c": "{{polymorphic}} {0} Beziehung: {1} erfordert den Parameter 'model'",
|
||||
"fca4d12faff1035d9d0438d73432571b": "Doppelter Eintrag für {0}.{1}",
|
||||
"fd3cc89dc67e2d604eaae21bdf41d403": "Beziehung {0} für Modell {1} konnte nicht gefunden werden",
|
||||
"fec8ebda24db46a9d040bf863765cc44": "Der Operator {0} weist ungültige Klauseln {1} auf: {2}"
|
||||
}
|
||||
|
|
@ -0,0 +1,96 @@
|
|||
{
|
||||
"0483a77cf77741504204e5c066597487": "{{polymorphic}} {0} relation: {1} does not expect param `polymorphic.as` when defing custom `foreignKey`/`discriminator` ",
|
||||
"09483e03b91c8bd58732a74b3ef1ec13": "Invalid date: {0}",
|
||||
"0a5aa17f7866a85e3aee37ef5369403c": "LinkManyToMany received target that doesn't contain required \"{0}\"",
|
||||
"0b16d3ffc42f91b4b9a4b3b50c41c838": "The order {0} is not valid",
|
||||
"0bd753a8944ad0af85a939bb25273887": "Cannot expire unknown key {0}",
|
||||
"0c0b867aca0973ba26e887d3337cc4ec": "{{Polymorphic}} model not found: `{0}` not set",
|
||||
"0c4eb8b6c2ff6e51d7e195eee346ced9": "Table '{0}' does not exist.",
|
||||
"0ff31abb394afb555df162e74ff1a0a0": "{{id}} cannot be updated from {0} to {1} when {{forceId}} is set to true",
|
||||
"1ae7d3e0be381efb32bfd1ba652f5172": "WARNING: {{polymorphic}} {0} relation: {1} uses keyword `polymorphic.as` which will be DEPRECATED in LoopBack.next, refer to this doc for replacement solutions (https://loopback.io/doc/en/lb3/Polymorphic-relations.html#deprecated-polymorphic-as)",
|
||||
"1daef4e937fe52136597ba8fd2060f55": "Nesting transactions is not supported",
|
||||
"21095484501dbff31af6556fa6039182": "The {{offset/skip}} parameter {0} is not valid",
|
||||
"280f4550f90e133118955ec6f6f72830": "Discriminator type {0} specified but no model exists with such name",
|
||||
"28697ec15968a7969211f6d035ba9260": "{{polymorphic}} {0} relation: {1} does not expect param `model`",
|
||||
"2c4904377a87fdab502118719cc0d266": "{{Transaction}} is not supported",
|
||||
"2c5c8519721f749aab13c2f04f41d611": "The {0} property has invalid clause {1}: Expected precisely 2 values, received {2}",
|
||||
"2f4af31c144bbfab1bbf479866acd820": "\nWARNING: {{LoopBack}} connector \"{0}\" is not installed as any of the following modules:\n\n {1}\n\nTo fix, run:\n\n {{npm install {2} --save}}\n",
|
||||
"3864f9be10f27723074566d2b3893514": "Warning: Model {0}, {{strict mode: `throw`}} has been removed, please use {{`strict: true`}} instead, which returns{{`Validation Error`}} for unknown properties,",
|
||||
"38dbf42c29a4645238cc3d632e88ebc9": "{{Relation.modelTo}} is not defined for relation {0} and is no {{polymorphic}}",
|
||||
"3cde8cc9bca22c67278b202ab0720106": "No instance with id {0} found for {1}",
|
||||
"416dfbb7b823f51c9f3800be81060b41": "No instance with {{id}} {0} found for {1}",
|
||||
"49b5afd8c6a19ad9c8abeffb2f8114eb": "BelongsTo method \"getAsync()\" is deprecated, use \"get()\" instead.",
|
||||
"4c78325cedbb826db3a05bf5df0e8546": "You must provide an {{id}} when replacing!",
|
||||
"4e31b1edd10dadb724d83387de0b5062": "The {{limit}} parameter {0} is not valid",
|
||||
"514985b2327f061ffb1c932f6b909979": "Model {0} is not defined.",
|
||||
"525c856e65daab43be247e7b5410febd": "{{polymorphic}} {0} relation: {1} does not expect param `polymorphic.selector` when defing custom `foreignKey`/`discriminator` ",
|
||||
"5c18ee111dd87540cdb19a2a93b33be9": "Transaction is rolled back due to timeout",
|
||||
"5ec7e6664256f7ea78f4f06dafc7d974": "Transaction is not ready, wait for the returned promise to resolve",
|
||||
"5ec8efeb715a2c34b440f2d76e2cf87d": " {0}",
|
||||
"6111399276924ffa3bc9a410cdfcb2e5": "No {{id}} name {0}",
|
||||
"614e3355647e4127c96256102dc63376": "The {0} property has invalid clause {1}: Expected a string or RegExp",
|
||||
"62a2d80c405b7fec5f547c448ab1b6ff": "The {{order}} {0} has invalid direction",
|
||||
"6502a117987610380b9068ef98b1b0ee": "No record found in {0} for ({1}.{2} ,{3}.{4})",
|
||||
"67c2bf43b5281ab929617423ea8a6f3e": "The connector {0} does not support {{replaceById}} operation. This is not a bug in LoopBack. Please contact the authors of the connector, preferably via GitHub issues.",
|
||||
"6c3234937d69763fc7f6bcafccc59bbc": "{{Model::deleteById}} requires the {{id}} argument",
|
||||
"6eb6fd4fbd73394000bc25f5776fd20c": "{{Model::exists}} requires the {{id}} argument",
|
||||
"6fcc2ff0db7a4f490f5e0ce9e24691f3": "{{HasOne}} relation cannot create more than one instance of {0}",
|
||||
"728232e473bf80272c042df2b7e002f4": "{{polymorphic}} {0} relation: {1} requires param `polymorphic.discriminator` when param `polymorphic.foreignKey` is provided",
|
||||
"791ab3031a73ede03f7d6299a85e8289": "Timeout in connecting after {0} ms",
|
||||
"7b277018e43d41bc445731092b91547d": "Not connected",
|
||||
"7bbbdece4eea90e42aa5c0bce295e503": "{{Model::findById}} requires the {{id}} argument",
|
||||
"7e9530c0399289be0ee601a604be71ff": "{{BelongsTo}} relation {0} is empty",
|
||||
"7faa840eb6ce11250a141deb42a6c489": "Unknown relation {{scope}}: {0}",
|
||||
"8091838319a5cc7d6a34af2f2a616ce9": "Property name should not be \"{{constructor}}\" in Model: {0}",
|
||||
"881e4b0cb86ed59549248ee540a9fd10": "Property name \"{{constructor}}\" is not allowed in {0} data",
|
||||
"89afd3a9249f5a8d3edda07d84ca049d": "{{Polymorphic}} model not found: `{0}`",
|
||||
"89bf6d92731fe7bd2146ce8d0bec205c": "Invalid argument, must be a string, {{regex}} literal, or {{RegExp}} object",
|
||||
"8a39126103a157f501affa070367a1b0": "The {0} instance is not valid. Details: {1}.",
|
||||
"8c5ab01638c1ac1d58168c6346a8481a": "Invalid {{regex}} flags: {0}",
|
||||
"938401ea4ce48159efa9be1d4a5e8bab": "Items must be an array: {0}",
|
||||
"9e1f143ee02946324d34da92f71bf74e": "{0} relation: {1} requires param `model`",
|
||||
"a004f310d315e592843776fab964eaeb": "{{Polymorphic}} relations need a through model",
|
||||
"a0cf0e09c26df14283223e84e6a10f00": "Could not update attributes. {{Object}} with {{id}} {0} does not exist!",
|
||||
"a2487abefef4259c2131d96cdb8543b1": "Connection fails: {0}\nIt will be retried for the next request.",
|
||||
"a25e41a39c60c4702e55d0c3936576a1": "Key mismatch: {0}.{1}: {2}, {3}.{4}: {5}",
|
||||
"a327355560d495454fba2c1aad6bdf09": "Unknown scope method: {0}",
|
||||
"a6c18a7f4390cd3d59a2a7a047ae2aab": "Run \"{{npm install loopback-datasource-juggler}} {0}\" command ",
|
||||
"a829dee089c912e68c18920ba015400c": "WARNING: {{id}} property cannot be changed from {0} to {1} for model:{2} in {{'loaded'}} operation hook",
|
||||
"a984a076c59e451948b2bcf7a393d860": "WARNING: {{id}} property cannot be changed from {0} to {1} for model:{2} in {{'before save'}} operation hook",
|
||||
"ac04cf275b71c1eb89a41cf6bbad7a64": "HasOne method \"getAsync()\" is deprecated, use \"get()\" instead.",
|
||||
"b138294f132edfe1eb2a8211150c7238": "Unexpected `undefined` in query",
|
||||
"b15b20280211ad258d92947f05b6e4a5": "The connector has not been initialized.",
|
||||
"b278876ec93ef9760f00e83f38ba313d": "Scope method \"getAsync()\" is deprecated, use \"find()\" instead.",
|
||||
"ba0fd8106eb54de4d003a844206431fd": "Model hook \"{0}\" is deprecated, use Operation hooks instead. {{http://docs.strongloop.com/display/LB/Operation+hooks}}",
|
||||
"baf2c8b0c5a574b8a894e9b6304fece1": "The where clause {0} is not an {{object}}",
|
||||
"bdb11cc1c780c9ccac33c316cfdc9d82": "Type not defined for property {0}.{1}",
|
||||
"bdfb951c8ff7ce0cbc08c06f548fd927": "Value is an empty {{object}}",
|
||||
"bec226891a505828bfc76c5cfd73b336": "Cannot get TTL for unknown key {0}",
|
||||
"cd930369e86cdd222f7bd117c6f9fa94": "Unknown default value provider {0}",
|
||||
"cfee4d8149316d9a647c0885cf3cafaf": "Property names containing dot(s) are not supported. Model: {0}, dynamic property: {1}",
|
||||
"d40328eabd8756d795bcdd49d782d4e9": "DataSource does not support transactions",
|
||||
"da02dd6c53d4148320eeb31718a7aebe": "Invalid type for property {0}",
|
||||
"da751a8a748adbde5b55fa83b707b4e2": "Property names containing dot(s) are not supported. Model: {0}, property: {1}",
|
||||
"db03083e9a768388fdbee865249ac67a": "Ignoring validation errors in {{updateOrCreate()}}:",
|
||||
"dd63416d9b7d9fa4181e89efd619dfd8": "Value is not an {{array}} or {{object}} with sequential numeric indices",
|
||||
"ddf0aa14803f1c84f4a97f3803f7471c": "Class name required",
|
||||
"e08ab0e1ab55f26c357061447b635905": "No relation found in {0} for ({1}.{2},{3}.{4})",
|
||||
"e0e9504e137a3c3339144b51ed76fef2": "Connector is not defined correctly: it should create `{{connector}}` member of dataSource",
|
||||
"e2f282cbe3efba001d6d3a09f7f6ca8c": "{{polymorphic}} {0} relation: {1} requires param `polymorphic.foreignKey` when param `polymorphic.discriminator` is provided",
|
||||
"e39e0f5d52bfbf511e645d19ecadd2fa": "The {0} property has invalid clause {1}: {2}",
|
||||
"e4434de4bb8f5a3cd1d416e4d80d7e0b": "Unknown \"{0}\" {{id}} \"{1}\".",
|
||||
"e54d944c2a2c85a23caa86027ae307cf": "Cannot migrate models not attached to this datasource: {0}",
|
||||
"e54f118a75e15e132f16b985274eb46d": "The query filter {0} is not an {{object}}",
|
||||
"e55937649d8d7a11706b8cec22d02eae": "{{HasOne}} relation {0} is empty",
|
||||
"e6161ae8459c79d810e2aa9d21282a39": "You must provide an {{id}} when updating attributes!",
|
||||
"eb56c2b0c30cf006e2df00a549ec9c2c": "Relation \"{0}\" is not defined for {1} model",
|
||||
"ec42dca074f1818c447f7ad16e2d01af": "{0} is not provided by the attached connector",
|
||||
"ecb7aa804bf54c682999d20d6436104c": "The {{transaction}} is not active: {0}",
|
||||
"f30809cb932b72a66416a709c8531530": "The connector does not support {{method}} within a transaction",
|
||||
"f41bd91dc0f000a79c0bf842f1b7fdf9": "could not create List from JSON string: {0}",
|
||||
"f6e8c96c93b9c7687d6c172b3695e898": "{{id}} property ({0}) cannot be updated from {1} to {2}",
|
||||
"fa9ae17e8e008d0eb0f0421a2972308c": "{{polymorphic}} {0} relation: {1} requires param `model`",
|
||||
"fca4d12faff1035d9d0438d73432571b": "Duplicate entry for {0}.{1}",
|
||||
"fd3cc89dc67e2d604eaae21bdf41d403": "Could not find relation {0} for model {1}",
|
||||
"fec8ebda24db46a9d040bf863765cc44": "The {0} operator has invalid clauses {1}: {2}"
|
||||
}
|
|
@ -0,0 +1,97 @@
|
|||
{
|
||||
"0483a77cf77741504204e5c066597487": "Relación {0} {{polymorphic}}: {1} no espera el parámetro `polymorphic.as` al definir `foreignKey`/`discriminator` personalizado ",
|
||||
"09483e03b91c8bd58732a74b3ef1ec13": "Fecha no válida: {0}",
|
||||
"0a5aa17f7866a85e3aee37ef5369403c": "LinkManyToMany ha recibido un destino que no contiene el \"{0}\" obligatorio",
|
||||
"0b16d3ffc42f91b4b9a4b3b50c41c838": "El orden {0} no es válido",
|
||||
"0bd753a8944ad0af85a939bb25273887": "La clave desconocida {0} no puede caducar",
|
||||
"0c0b867aca0973ba26e887d3337cc4ec": "Modelo {{Polymorphic}} no encontrado: `{0}` no establecido",
|
||||
"0c4eb8b6c2ff6e51d7e195eee346ced9": "La tabla '{0}' no existe.",
|
||||
"0ff31abb394afb555df162e74ff1a0a0": "{{id}} no puede actualizarse de {0} a {1} cuando {{forceId}} está establecido en true",
|
||||
"1ae7d3e0be381efb32bfd1ba652f5172": "AVISO: Relación {0} {{polymorphic}}: {1} utiliza la c¡palabra clave `polymorphic.as` que estará EN DESUSO en LoopBack.next, reconsulte este documento para conocer las posibles sustituciones (https://loopback.io/doc/en/lb3/Polymorphic-relations.html#deprecated-polymorphic-as)",
|
||||
"1daef4e937fe52136597ba8fd2060f55": "Las transacciones anidadas no están soportadas",
|
||||
"21095484501dbff31af6556fa6039182": "El parámetro de {{offset/skip}} {0} no es válido",
|
||||
"280f4550f90e133118955ec6f6f72830": "Se ha especificado el tipo de discriminador {0}, pero no existe ningún modelo con ese nombre",
|
||||
"28697ec15968a7969211f6d035ba9260": "Relación {0} {{polymorphic}}: {1} no espera el parámetro `model`",
|
||||
"2c4904377a87fdab502118719cc0d266": "{{Transaction}} no está soportada.",
|
||||
"2c5c8519721f749aab13c2f04f41d611": "La propiedad {0} tiene una cláusula {1} no válida: se esperaban exactamente 2 valores, se han recibido {2}",
|
||||
"2f4af31c144bbfab1bbf479866acd820": "\nAVISO: el conector {{LoopBack}} \"{0}\" no está instalado como ninguno de los módulos siguientes:\n\n {1}\n\nPara solucionarlo, ejecute:\n\n {{npm install {2} --save}}\n",
|
||||
"3864f9be10f27723074566d2b3893514": "Aviso: el modelo {0}, {{strict mode: `throw`}} se ha eliminado; utilice {{`strict: true`}} en su lugar, que devuelve {{`Validation Error`}} para propiedades desconocidas.",
|
||||
"38dbf42c29a4645238cc3d632e88ebc9": "{{Relation.modelTo}} no se ha definido para la relación {0} y no es {{polymorphic}}",
|
||||
"3cde8cc9bca22c67278b202ab0720106": "No se ha encontrado ninguna instancia con el ID {0} para {1}",
|
||||
"416dfbb7b823f51c9f3800be81060b41": "No se ha encontrado ninguna instancia con el {{id}} {0} para {1}",
|
||||
"49b5afd8c6a19ad9c8abeffb2f8114eb": "El método BelongsTo \"getAsync()\" está en desuso, utilice \"get()\" en su lugar.",
|
||||
"4c78325cedbb826db3a05bf5df0e8546": "Debe proporcionar un {{id}} al sustituir.",
|
||||
"4e31b1edd10dadb724d83387de0b5062": "El parámetro de {{limit}} {0} no es válido",
|
||||
"514985b2327f061ffb1c932f6b909979": "El modelo {0} no se ha definido.",
|
||||
"525c856e65daab43be247e7b5410febd": "Relación {0} {{polymorphic}}: {1} no espera el parámetro `polymorphic.selector` al definir `foreignKey`/`discriminator` personalizado ",
|
||||
"5c18ee111dd87540cdb19a2a93b33be9": "La transacción se ha retrotraído debido a tiempo de espera excedido",
|
||||
"5ec7e6664256f7ea78f4f06dafc7d974": "La transacción no está lista, espere la promesa devuelta para resolver",
|
||||
"5ec8efeb715a2c34b440f2d76e2cf87d": " {0}",
|
||||
"6111399276924ffa3bc9a410cdfcb2e5": "Ningún {{id}} de nombre {0}",
|
||||
"614e3355647e4127c96256102dc63376": "La propiedad {0} tiene una cláusula {1} no válida: se esperaba una serie o expresión regular",
|
||||
"62a2d80c405b7fec5f547c448ab1b6ff": "El {{order}} {0} tiene una dirección no válida",
|
||||
"6502a117987610380b9068ef98b1b0ee": "No se ha encontrado ningún registro en {0} para ({1}.{2} ,{3}.{4})",
|
||||
"67c2bf43b5281ab929617423ea8a6f3e": "El conector {0} no admite la operación {{replaceById}}. Esto no es un error en LoopBack. Póngase en contacto con los autores del conector, preferiblemente a través de cuestiones GitHub.",
|
||||
"6c3234937d69763fc7f6bcafccc59bbc": "{{Model::deleteById}} requiere el argumento {{id}}",
|
||||
"6eb6fd4fbd73394000bc25f5776fd20c": "{{Model::exists}} requiere el argumento {{id}}",
|
||||
"6fcc2ff0db7a4f490f5e0ce9e24691f3": "La relación {{HasOne}} no puede crear más de una instancia de {0}",
|
||||
"728232e473bf80272c042df2b7e002f4": "Relación {0} {{polymorphic}}: {1} requiere el parámetro `polymorphic.discriminator` cuando se proporciona el parámetro `polymorphic.foreignKey`",
|
||||
"791ab3031a73ede03f7d6299a85e8289": "Tiempo de espera agotado al conectarse después de {0} ms",
|
||||
"7b277018e43d41bc445731092b91547d": "No conectado",
|
||||
"7bbbdece4eea90e42aa5c0bce295e503": "{{Model::findById}} requiere el argumento {{id}}",
|
||||
"7e9530c0399289be0ee601a604be71ff": "La relación {{BelongsTo}} {0} está vacía",
|
||||
"7faa840eb6ce11250a141deb42a6c489": "Relación desconocida {{scope}}: {0}",
|
||||
"8091838319a5cc7d6a34af2f2a616ce9": "El nombre de propiedad no debe ser \"{{constructor}}\" en el modelo: {0}",
|
||||
"881e4b0cb86ed59549248ee540a9fd10": "El nombre de propiedad \"{{constructor}}\" no está permitido en datos de {0}",
|
||||
"89afd3a9249f5a8d3edda07d84ca049d": "Modelo {{Polymorphic}} no encontrado: `{0}`",
|
||||
"89bf6d92731fe7bd2146ce8d0bec205c": "Argumento no válido, debe ser una serie, literal de {{regex}} u objeto {{RegExp}}",
|
||||
"8a39126103a157f501affa070367a1b0": "La instancia {0} no es válida. Detalles: {1}.",
|
||||
"8c5ab01638c1ac1d58168c6346a8481a": "Distintivos de {{regex}} no válidos: {0}",
|
||||
"938401ea4ce48159efa9be1d4a5e8bab": "Items debe ser una matriz: {0}",
|
||||
"9e1f143ee02946324d34da92f71bf74e": "Relación {0}: {1} requiere el parámetro `model`",
|
||||
"a004f310d315e592843776fab964eaeb": "Las relaciones {{Polymorphic}} necesitan un modelo definido",
|
||||
"a0cf0e09c26df14283223e84e6a10f00": "No se han podido actualizar atributos. {{Object}} con {{id}} {0} no existe.",
|
||||
"a2487abefef4259c2131d96cdb8543b1": "La conexión falla: {0}\nSe reintentará en la siguiente solicitud.",
|
||||
"a25e41a39c60c4702e55d0c3936576a1": "Discrepancia de claves: {0}.{1}: {2}, {3}.{4}: {5}",
|
||||
"a327355560d495454fba2c1aad6bdf09": "Método de ámbito desconocido: {0}",
|
||||
"a6c18a7f4390cd3d59a2a7a047ae2aab": "Ejecute el mandato \"{{npm install loopback-datasource-juggler}} {0}\" ",
|
||||
"a829dee089c912e68c18920ba015400c": "AVISO: la propiedad {{id}} no puede cambiarse de {0} a {1} para el modelo:{2} en el gancho de operación {{'loaded'}}",
|
||||
"a984a076c59e451948b2bcf7a393d860": "AVISO: la propiedad {{id}} no puede cambiarse de {0} a {1} para el modelo:{2} en el gancho de operación {{'before save'}}",
|
||||
"ac04cf275b71c1eb89a41cf6bbad7a64": "El método HasOne \"getAsync()\" está en desuso, utilice \"get()\" en su lugar.",
|
||||
"b138294f132edfe1eb2a8211150c7238": "`undefined` inesperado en la consulta",
|
||||
"b15b20280211ad258d92947f05b6e4a5": "El conector no se ha inicializado.",
|
||||
"b278876ec93ef9760f00e83f38ba313d": "El método Scope \"getAsync()\" está en desuso, utilice \"find()\" en su lugar.",
|
||||
"ba0fd8106eb54de4d003a844206431fd": "El gancho de modelo \"{0}\" está en desuso, utilice ganchos de operación en su lugar. {{http://docs.strongloop.com/display/LB/Operation+hooks}}",
|
||||
"baf2c8b0c5a574b8a894e9b6304fece1": "La cláusula where {0} no es un {{object}}",
|
||||
"bdb11cc1c780c9ccac33c316cfdc9d82": "Tipo no definido para la propiedad {0}.{1}",
|
||||
"bdfb951c8ff7ce0cbc08c06f548fd927": "El valor es un {{object}} vacío",
|
||||
"bec226891a505828bfc76c5cfd73b336": "No se puede obtener TTL para la clave desconocida {0}",
|
||||
"cd930369e86cdd222f7bd117c6f9fa94": "Proveedor de valor predeterminado desconocido {0}",
|
||||
"cfee4d8149316d9a647c0885cf3cafaf": "Los nombres de propiedad que contienen puntos no están soportados. Modelo: {0}, propiedad dinámica: {1}",
|
||||
"d40328eabd8756d795bcdd49d782d4e9": "DataSource no da soporte a las transacciones",
|
||||
"da02dd6c53d4148320eeb31718a7aebe": "Tipo no válido para la propiedad {0}",
|
||||
"da751a8a748adbde5b55fa83b707b4e2": "Los nombres de propiedad que contienen puntos no están soportados. Modelo: {0}, propiedad: {1}",
|
||||
"db03083e9a768388fdbee865249ac67a": "Se ignoran los errores de validación en {{updateOrCreate()}}:",
|
||||
"dd63416d9b7d9fa4181e89efd619dfd8": "El valor no es una {{array}} u {{object}} con índices numéricos secuenciales",
|
||||
"ddf0aa14803f1c84f4a97f3803f7471c": "Nombre de clase obligatorio",
|
||||
"e08ab0e1ab55f26c357061447b635905": "No se ha encontrado ninguna relación en {0} para ({1}.{2},{3}.{4})",
|
||||
"e0e9504e137a3c3339144b51ed76fef2": "El conector no está definido correctamente: debe crear el miembro `{{connector}}` de dataSource",
|
||||
"e2f282cbe3efba001d6d3a09f7f6ca8c": "Relación {0} {{polymorphic}}: {1} requiere el parámetro `polymorphic.foreignKey` cuando se proporciona el parámetro `polymorphic.discriminator`",
|
||||
"e39e0f5d52bfbf511e645d19ecadd2fa": "La propiedad {0} tiene una cláusula {1} no válida: {2}",
|
||||
"e4434de4bb8f5a3cd1d416e4d80d7e0b": "{{id}} de \"{0}\" desconocido \"{1}\".",
|
||||
"e54d944c2a2c85a23caa86027ae307cf": "No se pueden migrar modelos no conectados a este origen de datos: {0}",
|
||||
"e54f118a75e15e132f16b985274eb46d": "El filtro de consultas {0} no es un {{object}}",
|
||||
"e55937649d8d7a11706b8cec22d02eae": "la relación {{HasOne}} {0} está vacía",
|
||||
"e6161ae8459c79d810e2aa9d21282a39": "Debe proporcionar un {{id}} al actualizar atributos.",
|
||||
"eb56c2b0c30cf006e2df00a549ec9c2c": "La relación \"{0}\" no está definida para el modelo {1}",
|
||||
"ec42dca074f1818c447f7ad16e2d01af": "El conector asociado no ha proporcionado {0}",
|
||||
"ecb7aa804bf54c682999d20d6436104c": "La {{transaction}} no está activa: {0}",
|
||||
"f30809cb932b72a66416a709c8531530": "El conector no admite {{method}} dentro de una transacción",
|
||||
"f41bd91dc0f000a79c0bf842f1b7fdf9": "no se ha podido crear la lista a partir de la serie JSON: {0}",
|
||||
"f6e8c96c93b9c7687d6c172b3695e898": "La propiedad de {{id}} ({0}) no puede actualizarse de {1} a {2}",
|
||||
"fa9ae17e8e008d0eb0f0421a2972308c": "Relación {0}:{{polymorphic}}: {1} requiere el parámetro `model`",
|
||||
"fca4d12faff1035d9d0438d73432571b": "Entrada duplicada para {0}.{1}",
|
||||
"fd3cc89dc67e2d604eaae21bdf41d403": "No se ha podido encontrar la relación {0} para el modelo {1}",
|
||||
"fec8ebda24db46a9d040bf863765cc44": "El operador {0} tiene cláusulas no válidas {1}: {2}"
|
||||
}
|
||||
|
|
@ -0,0 +1,97 @@
|
|||
{
|
||||
"0483a77cf77741504204e5c066597487": "Relation {{polymorphic}} {0} : {1} n'attend pas le paramètre `polymorphic.as` lors de la définition de `foreignKey`/`discriminator` personnalisé ",
|
||||
"09483e03b91c8bd58732a74b3ef1ec13": "Date non valide : {0}",
|
||||
"0a5aa17f7866a85e3aee37ef5369403c": "LinkManyToMany a reçu une cible qui ne contient pas \"{0}\" (requis)",
|
||||
"0b16d3ffc42f91b4b9a4b3b50c41c838": "L'ordre {0} n'est pas valide",
|
||||
"0bd753a8944ad0af85a939bb25273887": "Impossible de faire expirer la clé inconnue {0}",
|
||||
"0c0b867aca0973ba26e887d3337cc4ec": "Modèle {{Polymorphic}} non trouvé : `{0}` non défini",
|
||||
"0c4eb8b6c2ff6e51d7e195eee346ced9": "La table '{0}' n'existe pas.",
|
||||
"0ff31abb394afb555df162e74ff1a0a0": "{{id}} ne peut pas être mis à jour depuis {0} vers {1} lorsque {{forceId}} n'a pas la valeur true",
|
||||
"1ae7d3e0be381efb32bfd1ba652f5172": "AVERTISSEMENT : relation {{polymorphic}} {0} : {1} utilise le mot clé `polymorphic.as` qui sera OBSOLETE dans LoopBack.next. Reportez-vous à cette documentation pour découvrir les solutions de remplacement (https://loopback.io/doc/en/lb3/Polymorphic-relations.html#deprecated-polymorphic-as)",
|
||||
"1daef4e937fe52136597ba8fd2060f55": "L'imbrication des transactions n'est pas prise en charge ",
|
||||
"21095484501dbff31af6556fa6039182": "Le paramètre {{offset/skip}} {0} n'est pas valide",
|
||||
"280f4550f90e133118955ec6f6f72830": "Le type de discriminateur {0} est indiqué mais il n'existe pas de modèle de ce nom",
|
||||
"28697ec15968a7969211f6d035ba9260": "Relation {{polymorphic}} {0} : {1} n'attend pas le paramètre `model`",
|
||||
"2c4904377a87fdab502118719cc0d266": "{{Transaction}} n'est pas pris en charge",
|
||||
"2c5c8519721f749aab13c2f04f41d611": "La propriété {0} comporte une clause non valide {1} : 2 valeurs étaient attendues, {2} reçue",
|
||||
"2f4af31c144bbfab1bbf479866acd820": "\nAVERTISSEMENT : Le connecteur {{LoopBack}} \"{0}\" n'est pas installé, tout comme les modules suivants :\n\n {1}\n\nPour corriger le problème, exécutez :\n\n {{npm install {2} --save}}\n",
|
||||
"3864f9be10f27723074566d2b3893514": "Avertissement : le modèle {0}, {{strict mode: `throw`}} a été retiré, utilisez {{`strict: true`}} à la place, ce qui renvoie {{`Validation Error`}} pour les propriétés inconnues,",
|
||||
"38dbf42c29a4645238cc3d632e88ebc9": "{{Relation.modelTo}} n'est pas défini pour la relation {0} et il n'est pas {{polymorphic}}",
|
||||
"3cde8cc9bca22c67278b202ab0720106": "Aucune instance avec l'id {0} trouvée pour {1}",
|
||||
"416dfbb7b823f51c9f3800be81060b41": "Aucune instance avec {{id}} {0} trouvée pour {1}",
|
||||
"49b5afd8c6a19ad9c8abeffb2f8114eb": "La méthode BelongsTo \"getAsync()\" est obsolète ; utilisez \"get()\" à la place.",
|
||||
"4c78325cedbb826db3a05bf5df0e8546": "Vous devez fournir un {{id}} lors du remplacement !",
|
||||
"4e31b1edd10dadb724d83387de0b5062": "Le paramètre {{limit}} {0} n'est pas valide",
|
||||
"514985b2327f061ffb1c932f6b909979": "Le modèle {0} n'est pas défini.",
|
||||
"525c856e65daab43be247e7b5410febd": "Relation {{polymorphic}} {0} : {1} n'attend pas le paramètre `polymorphic.selector` lors de la définition de `foreignKey`/`discriminator` personnalisé ",
|
||||
"5c18ee111dd87540cdb19a2a93b33be9": "La transaction est annulée car le délai d'attente a été dépassé ",
|
||||
"5ec7e6664256f7ea78f4f06dafc7d974": "La transaction n'est pas prête ; attendez que la promesse renvoyée soit résolue ",
|
||||
"5ec8efeb715a2c34b440f2d76e2cf87d": " {0}",
|
||||
"6111399276924ffa3bc9a410cdfcb2e5": "Aucun nom {{id}} {0}",
|
||||
"614e3355647e4127c96256102dc63376": "La propriété {0} comporte une clause non valide {1} : chaîne ou RegExp attendu",
|
||||
"62a2d80c405b7fec5f547c448ab1b6ff": "{{order}} {0} a une direction non valide",
|
||||
"6502a117987610380b9068ef98b1b0ee": "Aucun enregistrement trouvé dans {0} pour ({1}.{2},{3}.{4})",
|
||||
"67c2bf43b5281ab929617423ea8a6f3e": "Le connecteur {0} ne prend pas en charge l'opération {{replaceById}}. IL ne s'agit pas d'un bogue dans LoopBack. Prenez contact avec les auteurs du connecteur, de préférence via les incidents GitHub.",
|
||||
"6c3234937d69763fc7f6bcafccc59bbc": "{{Model::deleteById}} requiert l'argument {{id}}",
|
||||
"6eb6fd4fbd73394000bc25f5776fd20c": "{{Model::exists}} requiert l'argument {{id}}",
|
||||
"6fcc2ff0db7a4f490f5e0ce9e24691f3": "La relation {{HasOne}} ne peut pas créer plus d'une instance de {0}",
|
||||
"728232e473bf80272c042df2b7e002f4": "Relation {{polymorphic}} {0} : {1} requiert le paramètre `polymorphic.discriminator` si le paramètre `polymorphic.foreignKey` est fourni",
|
||||
"791ab3031a73ede03f7d6299a85e8289": "Expiration du délai de connexion après {0} ms",
|
||||
"7b277018e43d41bc445731092b91547d": "Non connecté",
|
||||
"7bbbdece4eea90e42aa5c0bce295e503": "{{Model::findById}} requiert l'argument {{id}}",
|
||||
"7e9530c0399289be0ee601a604be71ff": "La relation {{BelongsTo}} {0} est vide",
|
||||
"7faa840eb6ce11250a141deb42a6c489": "Relation inconnue {{scope}} : {0}",
|
||||
"8091838319a5cc7d6a34af2f2a616ce9": "Le nom de propriété ne doit pas être \"{{constructor}}\" dans le modèle : {0}",
|
||||
"881e4b0cb86ed59549248ee540a9fd10": "Le nom de propriété \"{{constructor}}\" n'est pas autorisé dans les données {0}",
|
||||
"89afd3a9249f5a8d3edda07d84ca049d": "Modèle {{Polymorphic}} introuvable : `{0}`",
|
||||
"89bf6d92731fe7bd2146ce8d0bec205c": "Argument non valide ; doit être une chaîne, un littéral {{regex}} ou un objet {{RegExp}}",
|
||||
"8a39126103a157f501affa070367a1b0": "L'instance {0} n'est pas valide. Détails : {1}.",
|
||||
"8c5ab01638c1ac1d58168c6346a8481a": "Indicateurs {{regex}} non valides : {0}",
|
||||
"938401ea4ce48159efa9be1d4a5e8bab": "Les éléments doivent être un tableau : {0}",
|
||||
"9e1f143ee02946324d34da92f71bf74e": "Relation {0} : {1} requiert le paramètre 'model'",
|
||||
"a004f310d315e592843776fab964eaeb": "Les relations {{Polymorphic}} ont besoin d'un modèle exhaustif",
|
||||
"a0cf0e09c26df14283223e84e6a10f00": "Impossible de mettre à jour les attributs. {{Object}} avec {{id}} {0} n'existe pas !",
|
||||
"a2487abefef4259c2131d96cdb8543b1": "Echec de la connexion : {0}\nUn nouvel essai sera effectué pour la demande suivante.",
|
||||
"a25e41a39c60c4702e55d0c3936576a1": "Non-concordance des clés : {0}.{1} : {2}, {3}.{4} : {5}",
|
||||
"a327355560d495454fba2c1aad6bdf09": "Méthode scope inconnue : {0}",
|
||||
"a6c18a7f4390cd3d59a2a7a047ae2aab": "Exécuter la commande \"{{npm install loopback-datasource-juggler}} {0}\" ",
|
||||
"a829dee089c912e68c18920ba015400c": "AVERTISSEMENT : la propriété {{id}} ne peut pas être modifiée de {0} en {1} pour le modèle :{2} dans le point d'ancrage d'opération {{'loaded'}}",
|
||||
"a984a076c59e451948b2bcf7a393d860": "AVERTISSEMENT : la propriété {{id}} ne peut pas être modifiée de {0} en {1} pour le modèle :{2} dans le point d'ancrage d'opération {{'before save'}}",
|
||||
"ac04cf275b71c1eb89a41cf6bbad7a64": "La méthode HasOne \"getAsync()\" est obsolète ; utilisez \"get()\" à la place.",
|
||||
"b138294f132edfe1eb2a8211150c7238": "`undefined` inattendu dans la requête",
|
||||
"b15b20280211ad258d92947f05b6e4a5": "Le connecteur n'a pas été initialisé.",
|
||||
"b278876ec93ef9760f00e83f38ba313d": "La méthode de portée \"getAsync()\" est obsolète ; utilisez \"find()\" à la place.",
|
||||
"ba0fd8106eb54de4d003a844206431fd": "Le point d'ancrage de modèle \"{0}\" est obsolète ; utilisez à la place les points d'ancrage d'opération. {{http://docs.strongloop.com/display/LB/Operation+hooks}}",
|
||||
"baf2c8b0c5a574b8a894e9b6304fece1": "La clause where {0} n'est pas un {{object}}",
|
||||
"bdb11cc1c780c9ccac33c316cfdc9d82": "Type non défini pour la propriété {0}.{1}",
|
||||
"bdfb951c8ff7ce0cbc08c06f548fd927": "La valeur est un {{object}} vide",
|
||||
"bec226891a505828bfc76c5cfd73b336": "Impossible d'obtenir TTL pour la clé inconnue {0}",
|
||||
"cd930369e86cdd222f7bd117c6f9fa94": "Fournisseur de valeur par défaut inconnu {0}",
|
||||
"cfee4d8149316d9a647c0885cf3cafaf": "Les noms de propriété contenant un ou plusieurs points ne sont pas pris en charge. Modèle : {0}, propriété dynamique : {1}",
|
||||
"d40328eabd8756d795bcdd49d782d4e9": "La source de données ne prend pas en charge les transactions ",
|
||||
"da02dd6c53d4148320eeb31718a7aebe": "Type non valide pour la propriété {0}",
|
||||
"da751a8a748adbde5b55fa83b707b4e2": "Les noms de propriété contenant un ou plusieurs points ne sont pas pris en charge. Modèle : {0}, propriété : {1}",
|
||||
"db03083e9a768388fdbee865249ac67a": "Erreurs de validation ignorées dans {{updateOrCreate()}} :",
|
||||
"dd63416d9b7d9fa4181e89efd619dfd8": "La valeur n'est pas {{array}} ou {{object}} avec des indices numériques séquentiels",
|
||||
"ddf0aa14803f1c84f4a97f3803f7471c": "Nom de classe obligatoire",
|
||||
"e08ab0e1ab55f26c357061447b635905": "Aucune relation trouvée dans {0} pour ({1}.{2},{3}.{4})",
|
||||
"e0e9504e137a3c3339144b51ed76fef2": "Le connecteur n'est pas défini correctement ; il devrait créer le membre `{{connector}}` de la source de données",
|
||||
"e2f282cbe3efba001d6d3a09f7f6ca8c": "Relation {{polymorphic}} {0} : {1} requiert le paramètre `polymorphic.foreignKey` si le paramètre `polymorphic.discriminator` est fourni",
|
||||
"e39e0f5d52bfbf511e645d19ecadd2fa": "La propriété {0} comporte une clause non valide {1} : {2}",
|
||||
"e4434de4bb8f5a3cd1d416e4d80d7e0b": "\"{0}\" {{id}} \"{1}\" inconnu.",
|
||||
"e54d944c2a2c85a23caa86027ae307cf": "Impossible de migrer les modèles non associés à cette source de données : {0}",
|
||||
"e54f118a75e15e132f16b985274eb46d": "Le filtre de requête {0} n'est pas un {{object}}",
|
||||
"e55937649d8d7a11706b8cec22d02eae": "La relation {{HasOne}} {0} est vide",
|
||||
"e6161ae8459c79d810e2aa9d21282a39": "Vous devez fournir un {{id}} lors de la mise à jour des attributs !",
|
||||
"eb56c2b0c30cf006e2df00a549ec9c2c": "La relation \"{0}\" n'est pas définie pour le modèle {1}",
|
||||
"ec42dca074f1818c447f7ad16e2d01af": "{0} n'est pas fourni par le connecteur associé",
|
||||
"ecb7aa804bf54c682999d20d6436104c": "{{transaction}} n'est pas actif : {0}",
|
||||
"f30809cb932b72a66416a709c8531530": "Le connecteur ne prend pas en charge {{method}} dans une transaction",
|
||||
"f41bd91dc0f000a79c0bf842f1b7fdf9": "impossible de créer la liste à partir de la chaîne JSON : {0}",
|
||||
"f6e8c96c93b9c7687d6c172b3695e898": "La propriété {{id}} ({0}) ne peut pas être mise à jour à partir de {1} vers {2}",
|
||||
"fa9ae17e8e008d0eb0f0421a2972308c": "Relation {{polymorphic}} {0} : {1} requiert le paramètre `model`",
|
||||
"fca4d12faff1035d9d0438d73432571b": "Entrée en double pour {0}.{1}",
|
||||
"fd3cc89dc67e2d604eaae21bdf41d403": "Relation {0} introuvable pour le modèle {1}",
|
||||
"fec8ebda24db46a9d040bf863765cc44": "L'opérateur {0} comporte des clauses non valides {1} : {2}"
|
||||
}
|
||||
|
|
@ -0,0 +1,97 @@
|
|||
{
|
||||
"0483a77cf77741504204e5c066597487": "{{polymorphic}} Relazione {0}: {1} non prevedere il parametro `polymorphic.as` durante la definizione di `foreignKey`/`discriminator` personalizzato ",
|
||||
"09483e03b91c8bd58732a74b3ef1ec13": "Data non valida: {0}",
|
||||
"0a5aa17f7866a85e3aee37ef5369403c": "LinkManyToMany ha ricevuto una destinazione che non contiene il valore \"{0}\" richiesto",
|
||||
"0b16d3ffc42f91b4b9a4b3b50c41c838": "L'ordine {0} non è valido",
|
||||
"0bd753a8944ad0af85a939bb25273887": "Impossibile fare scadere la chiave sconosciuta {0}",
|
||||
"0c0b867aca0973ba26e887d3337cc4ec": "Modello {{Polymorphic}} non trovato: `{0}` non impostato",
|
||||
"0c4eb8b6c2ff6e51d7e195eee346ced9": "La tabella '{0}' non esiste.",
|
||||
"0ff31abb394afb555df162e74ff1a0a0": "Impossibile aggiornare {{id}} da {0} a {1} quando {{forceId}} è impostato su true",
|
||||
"1ae7d3e0be381efb32bfd1ba652f5172": "AVVERTENZA: {{polymorphic}} La relazione {0}: {1} utilizza la parola chiave `polymorphic.as` che sarà OBSOLETA in LoopBack.next, fare riferimento a questa documentazione per le soluzioni di sostituzione (https://loopback.io/doc/en/lb3/Polymorphic-relations.html#deprecated-polymorphic-as)",
|
||||
"1daef4e937fe52136597ba8fd2060f55": "La nidificazione delle transazioni non è supportata",
|
||||
"21095484501dbff31af6556fa6039182": "Il parametro {{offset/skip}} {0} non è valido",
|
||||
"280f4550f90e133118955ec6f6f72830": "È stato specificato il tipo di discriminatore {0} ma non esiste alcun modello con tale nome",
|
||||
"28697ec15968a7969211f6d035ba9260": "{{polymorphic}} La relazione {0}: {1} non prevede il parametro `model`",
|
||||
"2c4904377a87fdab502118719cc0d266": "{{Transaction}} non supportata",
|
||||
"2c5c8519721f749aab13c2f04f41d611": "La proprietà {0} ha la clausola non valida {1}: Previsti esattamente 2 valori, ricevuti {2}",
|
||||
"2f4af31c144bbfab1bbf479866acd820": "\nAVVERTENZA: il connettore {{LoopBack}} \"{0}\" non è installato come nessuno dei seguenti moduli:\n\n {1}\n\nPer correggere, eseguire:\n\n {{npm install {2} --save}}\n",
|
||||
"3864f9be10f27723074566d2b3893514": "Avvertenza: il modello {0}, {{strict mode: `throw`}} è stato rimosso, utilizzare {{`strict: true`}}, che restituisce {{`Validation Error`}} per le proprietà sconosciute,",
|
||||
"38dbf42c29a4645238cc3d632e88ebc9": "{{Relation.modelTo}} non è definito per la relazione {0} e non è {{polymorphic}}",
|
||||
"3cde8cc9bca22c67278b202ab0720106": "Nessuna istanza con ID {0} trovata per {1}",
|
||||
"416dfbb7b823f51c9f3800be81060b41": "Nessuna istanza con {{id}} {0} trovata per {1}",
|
||||
"49b5afd8c6a19ad9c8abeffb2f8114eb": "Il metodo BelongsTo \"getAsync()\" è obsoleto, utilizzare \"get()\".",
|
||||
"4c78325cedbb826db3a05bf5df0e8546": "È necessario fornire un {{id}} durante la sostituzione.",
|
||||
"4e31b1edd10dadb724d83387de0b5062": "Il parametro {{limit}} {0} non è valido",
|
||||
"514985b2327f061ffb1c932f6b909979": "Il modello {0} non è definito.",
|
||||
"525c856e65daab43be247e7b5410febd": "{{polymorphic}} La relazione {0}: {1} non prevede il parametro `polymorphic.selector` durante la definizione di `foreignKey`/`discriminator` personalizzato ",
|
||||
"5c18ee111dd87540cdb19a2a93b33be9": "Viene effettuato il rollback della transazione a causa del timeout",
|
||||
"5ec7e6664256f7ea78f4f06dafc7d974": "La transazione non è pronta, attendere la promessa di risolvere restituita",
|
||||
"5ec8efeb715a2c34b440f2d76e2cf87d": " {0}",
|
||||
"6111399276924ffa3bc9a410cdfcb2e5": "Nessun nome {{id}} {0}",
|
||||
"614e3355647e4127c96256102dc63376": "La proprietà {0} ha una clausola non valida {1}: Prevista una stringa o RegExp",
|
||||
"62a2d80c405b7fec5f547c448ab1b6ff": "{{order}} {0} ha una direzione non valida",
|
||||
"6502a117987610380b9068ef98b1b0ee": "Nessun record trovato in {0} per ({1}.{2} ,{3}.{4})",
|
||||
"67c2bf43b5281ab929617423ea8a6f3e": "Il connettore {0} non supporta l'operazione {{replaceById}}. Questo non è un bug in LoopBack. Contattare gli autori del connettore, preferibilmente mediante GitHub.",
|
||||
"6c3234937d69763fc7f6bcafccc59bbc": "{{Model::deleteById}} richiede l'argomento {{id}}",
|
||||
"6eb6fd4fbd73394000bc25f5776fd20c": "{{Model::exists}} richiede l'argomento {{id}}",
|
||||
"6fcc2ff0db7a4f490f5e0ce9e24691f3": "La relazione {{HasOne}} non può creare più di una istanza di {0}",
|
||||
"728232e473bf80272c042df2b7e002f4": "{{polymorphic}} La relazione {0}: {1} richiede il parametro `polymorphic.discriminator` quando viene fornito il parametro `polymorphic.foreignKey`",
|
||||
"791ab3031a73ede03f7d6299a85e8289": "Timeout nella connessione dopo {0} ms",
|
||||
"7b277018e43d41bc445731092b91547d": "Non connesso",
|
||||
"7bbbdece4eea90e42aa5c0bce295e503": "{{Model::findById}} richiede l'argomento {{id}}",
|
||||
"7e9530c0399289be0ee601a604be71ff": "La relazione {{BelongsTo}} {0} è vuota",
|
||||
"7faa840eb6ce11250a141deb42a6c489": "Relazione sconosciuta {{scope}}: {0}",
|
||||
"8091838319a5cc7d6a34af2f2a616ce9": "Il nome della proprietà non deve essere \"{{constructor}}\" nel modello: {0}",
|
||||
"881e4b0cb86ed59549248ee540a9fd10": "Nome della proprietà \"{{constructor}}\" non consentito nei dati {0}",
|
||||
"89afd3a9249f5a8d3edda07d84ca049d": "Modello {{Polymorphic}} non trovato: `{0}`",
|
||||
"89bf6d92731fe7bd2146ce8d0bec205c": "Argomento non valido, deve essere una stringa, un valore letterale {{regex}} o un oggetto {{RegExp}}",
|
||||
"8a39126103a157f501affa070367a1b0": "L'istanza {0} non è valida. Dettagli: {1}.",
|
||||
"8c5ab01638c1ac1d58168c6346a8481a": "Indicatori {{regex}} non validi: {0}",
|
||||
"938401ea4ce48159efa9be1d4a5e8bab": "Gli elementi devono essere un array: {0}",
|
||||
"9e1f143ee02946324d34da92f71bf74e": "La relazione {0}: {1} richiede il parametro `model`",
|
||||
"a004f310d315e592843776fab964eaeb": "Le relazioni {{Polymorphic}} richiedono un modello di passaggio",
|
||||
"a0cf0e09c26df14283223e84e6a10f00": "Impossibile aggiornare gli attributi. {{Object}} con {{id}} {0} non esiste.",
|
||||
"a2487abefef4259c2131d96cdb8543b1": "Errore della connessione: {0}\nVerrà eseguito un nuovo tentativo per la richiesta successiva.",
|
||||
"a25e41a39c60c4702e55d0c3936576a1": "Mancata corrispondenza della chiave: {0}.{1}: {2}, {3}.{4}: {5}",
|
||||
"a327355560d495454fba2c1aad6bdf09": "Metodo dell'ambito sconosciuto: {0}",
|
||||
"a6c18a7f4390cd3d59a2a7a047ae2aab": "Eseguire il comando \"{{npm install loopback-datasource-juggler}} {0}\" ",
|
||||
"a829dee089c912e68c18920ba015400c": "AVVERTENZA: impossibile modificare la proprietà {{id}} da {0} a {1} per il modello:{2} nell'hook operazione {{'loaded'}}",
|
||||
"a984a076c59e451948b2bcf7a393d860": "AVVERTENZA: impossibile modificare la proprietà {{id}} da {0} a {1} per il modello:{2} nell'hook operazione {{'before save'}}",
|
||||
"ac04cf275b71c1eb89a41cf6bbad7a64": "Il metodo HasOne \"getAsync()\" è obsoleto, utilizzare \"get()\".",
|
||||
"b138294f132edfe1eb2a8211150c7238": "Elemento `undefined` non previsto nella query",
|
||||
"b15b20280211ad258d92947f05b6e4a5": "Il connettore non è stato inizializzato.",
|
||||
"b278876ec93ef9760f00e83f38ba313d": "Il metodo dell'ambito \"getAsync()\" è obsoleto, utilizzare \"find()\" invece.",
|
||||
"ba0fd8106eb54de4d003a844206431fd": "L'hook del modello \"{0}\" è obsoleto, utilizzare gli hook dell'operazione. {{http://docs.strongloop.com/display/LB/Operation+hooks}}",
|
||||
"baf2c8b0c5a574b8a894e9b6304fece1": "La clausola where {0} non è un {{object}}",
|
||||
"bdb11cc1c780c9ccac33c316cfdc9d82": "Tipo non definito per la proprietà {0}.{1}",
|
||||
"bdfb951c8ff7ce0cbc08c06f548fd927": "Il valore è un {{object}} vuoto",
|
||||
"bec226891a505828bfc76c5cfd73b336": "Impossibile acquisire TTL per la chiave sconosciuta {0}",
|
||||
"cd930369e86cdd222f7bd117c6f9fa94": "Provider del valore predefinito sconosciuto {0}",
|
||||
"cfee4d8149316d9a647c0885cf3cafaf": "I nomi delle proprietà che contengono punti non sono supportati. Modello: {0}, proprietà dinamica: {1}",
|
||||
"d40328eabd8756d795bcdd49d782d4e9": "DataSource non supporta le transazioni",
|
||||
"da02dd6c53d4148320eeb31718a7aebe": "Tipo non valido per la proprietà {0}",
|
||||
"da751a8a748adbde5b55fa83b707b4e2": "I nomi delle proprietà che contengono punti non sono supportati. Modello: {0}, proprietà: {1}",
|
||||
"db03083e9a768388fdbee865249ac67a": "Errore di convalida in {{updateOrCreate()}} ignorati:",
|
||||
"dd63416d9b7d9fa4181e89efd619dfd8": "Il valore non è un {{array}} o {{object}} con indici numerici sequenziali",
|
||||
"ddf0aa14803f1c84f4a97f3803f7471c": "Nome della classe obbligatorio",
|
||||
"e08ab0e1ab55f26c357061447b635905": "Nessuna relazione trovata in {0} per ({1}.{2},{3}.{4})",
|
||||
"e0e9504e137a3c3339144b51ed76fef2": "Il connettore non è definito correttamente: deve creare il membro `{{connector}}` di dataSource",
|
||||
"e2f282cbe3efba001d6d3a09f7f6ca8c": "{{polymorphic}} La relazione {0}: {1} richiede il parametro `polymorphic.foreignKey` quando viene fornito il parametro `polymorphic.discriminator`",
|
||||
"e39e0f5d52bfbf511e645d19ecadd2fa": "La proprietà {0} ha una clausola non valida {1}: {2}",
|
||||
"e4434de4bb8f5a3cd1d416e4d80d7e0b": "{{id}} \"{0}\" sconosciuto \"{1}\".",
|
||||
"e54d944c2a2c85a23caa86027ae307cf": "Impossibile migrare i modelli non allegati a questa origine dati: {0}",
|
||||
"e54f118a75e15e132f16b985274eb46d": "Il filtro della query {0} non è un {{object}}",
|
||||
"e55937649d8d7a11706b8cec22d02eae": "La relazione {{HasOne}} {0} è vuota",
|
||||
"e6161ae8459c79d810e2aa9d21282a39": "È necessario fornire un {{id}} durante l'aggiornamento degli attributi.",
|
||||
"eb56c2b0c30cf006e2df00a549ec9c2c": "Relazione \"{0}\" non definita per il modello {1}",
|
||||
"ec42dca074f1818c447f7ad16e2d01af": "{0} non fornito dal connettore collegato",
|
||||
"ecb7aa804bf54c682999d20d6436104c": "La {{transaction}} non è attiva: {0}",
|
||||
"f30809cb932b72a66416a709c8531530": "Il connettore non supporta {{method}} all'interno di una transazione",
|
||||
"f41bd91dc0f000a79c0bf842f1b7fdf9": "impossibile creare un elenco dalla stringa JSON: {0}",
|
||||
"f6e8c96c93b9c7687d6c172b3695e898": "Impossibile aggiornare la proprietà {{id}} ({0}) da {1} a {2}",
|
||||
"fa9ae17e8e008d0eb0f0421a2972308c": "{{polymorphic}} La relazione {0}: {1} richiede il parametro `model`",
|
||||
"fca4d12faff1035d9d0438d73432571b": "Voce duplicata per {0}.{1}",
|
||||
"fd3cc89dc67e2d604eaae21bdf41d403": "Impossibile trovare la relazione {0} per il modello {1}",
|
||||
"fec8ebda24db46a9d040bf863765cc44": "L'operatore {0} ha delle clausole non valide {1}: {2}"
|
||||
}
|
||||
|
|
@ -0,0 +1,97 @@
|
|||
{
|
||||
"0483a77cf77741504204e5c066597487": "{{polymorphic}} {0} 関係: {1} は、カスタムの foreignKey/discriminator`を定義する際は、パラメーター polymorphic.as`を予期していません ",
|
||||
"09483e03b91c8bd58732a74b3ef1ec13": "無効な日付: {0}",
|
||||
"0a5aa17f7866a85e3aee37ef5369403c": "LinkManyToMany は、必須の \"{0}\" が含まれていないターゲットを受信しました",
|
||||
"0b16d3ffc42f91b4b9a4b3b50c41c838": "順序 {0} が無効です",
|
||||
"0bd753a8944ad0af85a939bb25273887": "不明のキー {0} を期限切れにすることができません",
|
||||
"0c0b867aca0973ba26e887d3337cc4ec": "{{Polymorphic}} モデルが見つかりません: `{0}` が設定されていません",
|
||||
"0c4eb8b6c2ff6e51d7e195eee346ced9": "テーブル '{0}' が存在しません。",
|
||||
"0ff31abb394afb555df162e74ff1a0a0": "{{forceId}} が true に設定されている場合、{{id}} を {0} から {1} に更新することはできません",
|
||||
"1ae7d3e0be381efb32bfd1ba652f5172": "警告: {{polymorphic}} {0} 関係: {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": "{{polymorphic}} {0} 関係: {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": "{1} に関する ID {0} のインスタンスが見つかりません",
|
||||
"416dfbb7b823f51c9f3800be81060b41": "{1} に関する {{id}} {0} のインスタンスが見つかりません",
|
||||
"49b5afd8c6a19ad9c8abeffb2f8114eb": "BelongsTo のメソッド \"getAsync()\" は非推奨です。代わりに \"get()\" を使用してください。",
|
||||
"4c78325cedbb826db3a05bf5df0e8546": "置換するときは {{id}} を指定する必要があります。",
|
||||
"4e31b1edd10dadb724d83387de0b5062": "{{limit}} パラメーター {0} は無効です",
|
||||
"514985b2327f061ffb1c932f6b909979": "モデル {0} が定義されていません。",
|
||||
"525c856e65daab43be247e7b5410febd": "{{polymorphic}} {0} 関係: {1} は、カスタムの foreignKey/discriminator`を定義する際は、パラメーター polymorphic.selector`を予期していません ",
|
||||
"5c18ee111dd87540cdb19a2a93b33be9": "タイムアウトのため、トランザクションがロールバックされます",
|
||||
"5ec7e6664256f7ea78f4f06dafc7d974": "トランザクションの準備ができていません。返された Promise の解決をお待ちください",
|
||||
"5ec8efeb715a2c34b440f2d76e2cf87d": " {0}",
|
||||
"6111399276924ffa3bc9a410cdfcb2e5": "{{id}} 名 {0} がありません",
|
||||
"614e3355647e4127c96256102dc63376": "{0} プロパティーには無効な節 {1} があります。ストリングまたは正規表現を指定する必要があります",
|
||||
"62a2d80c405b7fec5f547c448ab1b6ff": "{{order}} {0} の方向が無効です",
|
||||
"6502a117987610380b9068ef98b1b0ee": "({1}.{2}、{3}.{4}) に関して {0} でレコードが見つかりません",
|
||||
"67c2bf43b5281ab929617423ea8a6f3e": "コネクター {0} では {{replaceById}} 操作はサポートされません。 これは LoopBack のバグではありません。 コネクターの作成者に (なるべく GitHub Issue を通して) 問い合わせてください。",
|
||||
"6c3234937d69763fc7f6bcafccc59bbc": "{{Model::deleteById}} には {{id}} 引数が必要です",
|
||||
"6eb6fd4fbd73394000bc25f5776fd20c": "{{Model::exists}} には {{id}} 引数が必要です",
|
||||
"6fcc2ff0db7a4f490f5e0ce9e24691f3": "{{HasOne}} 関係では {0} のインスタンスを複数作成することはできません",
|
||||
"728232e473bf80272c042df2b7e002f4": "{{polymorphic}} {0} 関係: {1} では、パラメーター polymorphic.foreignKey`を指定したときは、パラメーター polymorphic.discriminator`は必須です",
|
||||
"791ab3031a73ede03f7d6299a85e8289": "接続は {0} ミリ秒後にタイムアウトになります",
|
||||
"7b277018e43d41bc445731092b91547d": "未接続",
|
||||
"7bbbdece4eea90e42aa5c0bce295e503": "{{Model::findById}} には {{id}} 引数が必要です",
|
||||
"7e9530c0399289be0ee601a604be71ff": "{{BelongsTo}} 関係 {0} が空です",
|
||||
"7faa840eb6ce11250a141deb42a6c489": "不明な関係 {{scope}}: {0}",
|
||||
"8091838319a5cc7d6a34af2f2a616ce9": "モデル {0} のプロパティー名を \"{{constructor}}\" にすることはできません",
|
||||
"881e4b0cb86ed59549248ee540a9fd10": "{0} データではプロパティー名 \"{{constructor}}\" は許可されません",
|
||||
"89afd3a9249f5a8d3edda07d84ca049d": "{{Polymorphic}} モデルが見つかりません: `{0}`",
|
||||
"89bf6d92731fe7bd2146ce8d0bec205c": "引数が無効です。ストリング、{{regex}} リテラル、または {{RegExp}} オブジェクトでなければなりません",
|
||||
"8a39126103a157f501affa070367a1b0": "{0} インスタンスは無効です。 詳細: {1}。",
|
||||
"8c5ab01638c1ac1d58168c6346a8481a": "無効な {{regex}} フラグ: {0}",
|
||||
"938401ea4ce48159efa9be1d4a5e8bab": "項目は配列でなければなりません: {0}",
|
||||
"9e1f143ee02946324d34da92f71bf74e": "{0} 関係: {1} ではパラメーター`model は必須です`",
|
||||
"a004f310d315e592843776fab964eaeb": "{{Polymorphic}} 関係にはスルー・モデルが必要です",
|
||||
"a0cf0e09c26df14283223e84e6a10f00": "属性を更新できませんでした。 {{id}} {0} の {{Object}} は存在しません。",
|
||||
"a2487abefef4259c2131d96cdb8543b1": "接続失敗: {0}\n次の要求で再試行されます。",
|
||||
"a25e41a39c60c4702e55d0c3936576a1": "キーの不一致: {0}.{1}: {2}、{3}.{4}: {5}",
|
||||
"a327355560d495454fba2c1aad6bdf09": "不明なスコープ・メソッド: {0}",
|
||||
"a6c18a7f4390cd3d59a2a7a047ae2aab": "\"{{npm install loopback-datasource-juggler}} {0}\" コマンドを実行します ",
|
||||
"a829dee089c912e68c18920ba015400c": "警告: {{'loaded'}} 操作フックでモデル {2} の {{id}} プロパティーを {0} から {1} に変更することはできません",
|
||||
"a984a076c59e451948b2bcf7a393d860": "警告: {{'before save'}} 操作フックでモデル {2} の {{id}} プロパティーを {0} から {1} に変更することはできません",
|
||||
"ac04cf275b71c1eb89a41cf6bbad7a64": "HasOne のメソッド \"getAsync()\" は非推奨です。代わりに \"get()\" を使用してください。",
|
||||
"b138294f132edfe1eb2a8211150c7238": "照会内に予期しない `undefined` があります",
|
||||
"b15b20280211ad258d92947f05b6e4a5": "コネクターが初期化されていません。",
|
||||
"b278876ec93ef9760f00e83f38ba313d": "Scope のメソッド \"getAsync()\" は非推奨です。代わりに \"find()\" を使用してください。",
|
||||
"ba0fd8106eb54de4d003a844206431fd": "モデル・フック \"{0}\" は非推奨です。代わりに操作フックを使用してください。 {{http://docs.strongloop.com/display/LB/Operation+hooks}}",
|
||||
"baf2c8b0c5a574b8a894e9b6304fece1": "where 節 {0} が {{object}} ではありません",
|
||||
"bdb11cc1c780c9ccac33c316cfdc9d82": "プロパティー {0}.{1} にタイプが定義されていません",
|
||||
"bdfb951c8ff7ce0cbc08c06f548fd927": "値は空の {{object}} です",
|
||||
"bec226891a505828bfc76c5cfd73b336": "不明のキー {0} の TTL を取得できません",
|
||||
"cd930369e86cdd222f7bd117c6f9fa94": "不明なデフォルト値プロバイダー {0}",
|
||||
"cfee4d8149316d9a647c0885cf3cafaf": "ドットが含まれたプロパティー名はサポートされていません。 モデル: {0}、動的プロパティー: {1}",
|
||||
"d40328eabd8756d795bcdd49d782d4e9": "データ・ソースがトランザクションをサポートしていません",
|
||||
"da02dd6c53d4148320eeb31718a7aebe": "プロパティー {0} のタイプが無効です",
|
||||
"da751a8a748adbde5b55fa83b707b4e2": "ドットが含まれたプロパティー名はサポートされていません。 モデル: {0}、プロパティー: {1}",
|
||||
"db03083e9a768388fdbee865249ac67a": "{{updateOrCreate()}} での妥当性検査エラーを無視します:",
|
||||
"dd63416d9b7d9fa4181e89efd619dfd8": "値は、連続した数字の索引が含まれた {{array}} または {{object}} ではありません",
|
||||
"ddf0aa14803f1c84f4a97f3803f7471c": "クラス名は必須です",
|
||||
"e08ab0e1ab55f26c357061447b635905": "({1}.{2}、{3}.{4}) に関して {0} で関係が見つかりません",
|
||||
"e0e9504e137a3c3339144b51ed76fef2": "コネクターが正しく定義されていません: dataSource の `{{connector}}` メンバーを作成する必要があります",
|
||||
"e2f282cbe3efba001d6d3a09f7f6ca8c": "{{polymorphic}} {0} 関係: {1} では、パラメーター polymorphic.discriminator`を指定したときは、パラメーター polymorphic.foreignKey`は必須です",
|
||||
"e39e0f5d52bfbf511e645d19ecadd2fa": "{0} プロパティーには無効な節 {1} があります: {2}",
|
||||
"e4434de4bb8f5a3cd1d416e4d80d7e0b": "\"{0}\" {{id}} \"{1}\" が不明です。",
|
||||
"e54d944c2a2c85a23caa86027ae307cf": "このデータ・ソースに付加されていないモデルはマイグレーションできません: {0}",
|
||||
"e54f118a75e15e132f16b985274eb46d": "照会フィルター {0} が {{object}} ではありません",
|
||||
"e55937649d8d7a11706b8cec22d02eae": "{{HasOne}} 関係 {0} が空です",
|
||||
"e6161ae8459c79d810e2aa9d21282a39": "属性を更新するときは {{id}} を指定する必要があります。",
|
||||
"eb56c2b0c30cf006e2df00a549ec9c2c": "関係 \"{0}\" は {1} モデルに定義されていません",
|
||||
"ec42dca074f1818c447f7ad16e2d01af": "{0} は付加されたコネクターによって提供されません",
|
||||
"ecb7aa804bf54c682999d20d6436104c": "{{transaction}} がアクティブではありません: {0}",
|
||||
"f30809cb932b72a66416a709c8531530": "コネクターがトランザクション内の {{method}} をサポートしていません",
|
||||
"f41bd91dc0f000a79c0bf842f1b7fdf9": "JSON ストリングからリストを作成できませんでした: {0}",
|
||||
"f6e8c96c93b9c7687d6c172b3695e898": "{{id}} プロパティー ({0}) を {1} から {2} に更新できません",
|
||||
"fa9ae17e8e008d0eb0f0421a2972308c": "{{polymorphic}} {0} 関係: {1} ではパラメーター`model は必須です`",
|
||||
"fca4d12faff1035d9d0438d73432571b": "{0}.{1} のエントリーが重複しています",
|
||||
"fd3cc89dc67e2d604eaae21bdf41d403": "モデル {1} の関係 {0} が見つかりませんでした",
|
||||
"fec8ebda24db46a9d040bf863765cc44": "{0} 演算子には無効な節 {1} があります: {2}"
|
||||
}
|
||||
|
|
@ -0,0 +1,97 @@
|
|||
{
|
||||
"0483a77cf77741504204e5c066597487": "{{polymorphic}} {0} 관계: 사용자 정의 `foreignKey`/`discriminator` 정의 중에는 {1}에 `polymorphic.as` 매개변수가 필요하지 않음 ",
|
||||
"09483e03b91c8bd58732a74b3ef1ec13": "올바르지 않은 날짜: {0}",
|
||||
"0a5aa17f7866a85e3aee37ef5369403c": "LinkManyToMany에 필수 \"{0}\"이(가) 포함되지 않은 대상이 수신됨",
|
||||
"0b16d3ffc42f91b4b9a4b3b50c41c838": "{0} 순서가 올바르지 않음",
|
||||
"0bd753a8944ad0af85a939bb25273887": "알 수 없는 키 {0}을(를) 만료할 수 없음",
|
||||
"0c0b867aca0973ba26e887d3337cc4ec": "{{Polymorphic}} 모델을 찾을 수 없음: `{0}`이(가) 설정되지 않음",
|
||||
"0c4eb8b6c2ff6e51d7e195eee346ced9": "'{0}' 테이블이 없습니다.",
|
||||
"0ff31abb394afb555df162e74ff1a0a0": "{{forceId}}이(가) true로 설정된 경우 {{id}}을(를) {0}에서 {1}(으)로 업데이트할 수 없음",
|
||||
"1ae7d3e0be381efb32bfd1ba652f5172": "경고: {{polymorphic}} {0} 관계: {1}이(가) LoopBack에서 더 이상 사용되지 않을 `polymorphic.as` 키워드를 사용합니다. 대체 솔루션에 대해서는 다음 문서를 참조하십시오(https://loopback.io/doc/en/lb3/Polymorphic-relations.html#deprecated-polymorphic-as).",
|
||||
"1daef4e937fe52136597ba8fd2060f55": "중첩 트랜잭션은 지원되지 않음",
|
||||
"21095484501dbff31af6556fa6039182": "{{offset/skip}} 매개변수 {0}이(가) 올바르지 않음",
|
||||
"280f4550f90e133118955ec6f6f72830": "식별자 유형 {0}이(가) 지정되었지만 해당 이름의 모델이 없습니다.",
|
||||
"28697ec15968a7969211f6d035ba9260": "{{polymorphic}} {0} 관계: {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`}}이(가) 제거되었습니다. 알 수 없는 특성에 대해 {{`Validation Error`}}을(를) 리턴하는 {{`strict: true`}}을(를) 대신 사용하십시오.",
|
||||
"38dbf42c29a4645238cc3d632e88ebc9": "관계 {0}에 대해 {{Relation.modelTo}}이(가) 정의되지 않았으며 {{polymorphic}}이(가) 아닙니다.",
|
||||
"3cde8cc9bca22c67278b202ab0720106": "{1}에 대해 ID {0}의 인스턴스를 찾을 수 없음",
|
||||
"416dfbb7b823f51c9f3800be81060b41": "{1}에 대해 {{id}} {0}의 인스턴스를 찾을 수 없음",
|
||||
"49b5afd8c6a19ad9c8abeffb2f8114eb": "BelongsTo 메소드 \"getAsync()\"는 더 이상 사용되지 않습니다. 대신 \"get()\"을 사용하십시오.",
|
||||
"4c78325cedbb826db3a05bf5df0e8546": "바꾸는 경우 {{id}}을(를) 제공해야 합니다!",
|
||||
"4e31b1edd10dadb724d83387de0b5062": "{{limit}} 매개변수 {0}이(가) 올바르지 않음",
|
||||
"514985b2327f061ffb1c932f6b909979": "{0} 모델이 정의되지 않았습니다.",
|
||||
"525c856e65daab43be247e7b5410febd": "{{polymorphic}} {0} 관계: 사용자 정의 `foreignKey`/`discriminator` 정의 중에는 {1}에 `polymorphic.selector` 매개변수가 필요하지 않음 ",
|
||||
"5c18ee111dd87540cdb19a2a93b33be9": "제한시간 초과로 인해 트랜잭션이 롤백됨",
|
||||
"5ec7e6664256f7ea78f4f06dafc7d974": "트랜잭션이 준비되지 않았으니 리턴된 일정이 해결될 때까지 기다려 주십시오.",
|
||||
"5ec8efeb715a2c34b440f2d76e2cf87d": " {0}",
|
||||
"6111399276924ffa3bc9a410cdfcb2e5": "{{id}} 이름 {0}이(가) 아님",
|
||||
"614e3355647e4127c96256102dc63376": "{0} 특성에 올바르지 않은 절 {1}이(가) 있음: 문자열 또는 RegExp를 예상함",
|
||||
"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": "{{polymorphic}} {0} 관계: `polymorphic.foreignKey` 매개변수가 제공된 경우 {1}에는 `polymorphic.discriminator` 매개변수가 필요함",
|
||||
"791ab3031a73ede03f7d6299a85e8289": "{0}밀리초 후 연결 제한시간 초과",
|
||||
"7b277018e43d41bc445731092b91547d": "연결되지 않음",
|
||||
"7bbbdece4eea90e42aa5c0bce295e503": "{{Model::findById}}에 {{id}} 인수가 필요함",
|
||||
"7e9530c0399289be0ee601a604be71ff": "{{BelongsTo}} 관계 {0}이(가) 비어 있음",
|
||||
"7faa840eb6ce11250a141deb42a6c489": "알 수 없는 관계 {{scope}}: {0}",
|
||||
"8091838319a5cc7d6a34af2f2a616ce9": "모델에서 특성 이름이 \"{{constructor}}\"이어서는 안됨: {0}",
|
||||
"881e4b0cb86ed59549248ee540a9fd10": "{0} 데이터에서는 특성 이름 \"{{constructor}}\"이(가) 허용되지 않음",
|
||||
"89afd3a9249f5a8d3edda07d84ca049d": "{{Polymorphic}} 모델을 찾을 수 없음: `{0}`",
|
||||
"89bf6d92731fe7bd2146ce8d0bec205c": "올바르지 않은 인수가 문자열, {{regex}} 리터럴 또는 {{RegExp}} 오브젝트여야 합니다.",
|
||||
"8a39126103a157f501affa070367a1b0": "{0} 인스턴스가 올바르지 않습니다. 세부사항: {1}.",
|
||||
"8c5ab01638c1ac1d58168c6346a8481a": "올바르지 않은 {{regex}} 플래그: {0}",
|
||||
"938401ea4ce48159efa9be1d4a5e8bab": "항목이 배열이어야 함: {0}",
|
||||
"9e1f143ee02946324d34da92f71bf74e": "{0} 관계: {1}에 `model` 매개변수가 필요함",
|
||||
"a004f310d315e592843776fab964eaeb": "{{Polymorphic}} 관계에는 through 모델이 필요함",
|
||||
"a0cf0e09c26df14283223e84e6a10f00": "속성을 업데이트할 수 없습니다. {{id}} {0}의 {{Object}}이(가) 없습니다!",
|
||||
"a2487abefef4259c2131d96cdb8543b1": "연결 실패: {0}\n다음 요청에서 재시도됩니다.",
|
||||
"a25e41a39c60c4702e55d0c3936576a1": "키 불일치: {0}.{1}: {2}, {3}.{4}: {5}",
|
||||
"a327355560d495454fba2c1aad6bdf09": "알 수 없는 범위 메소드: {0}",
|
||||
"a6c18a7f4390cd3d59a2a7a047ae2aab": "\"{{npm install loopback-datasource-juggler}} {0}\" 명령 실행 ",
|
||||
"a829dee089c912e68c18920ba015400c": "경고: {{'loaded'}} 오퍼레이션 후크에서 모델:{2}에 대해 {{id}} 특성을 {0}에서 {1}(으)로 변경할 수 없습니다.",
|
||||
"a984a076c59e451948b2bcf7a393d860": "경고: {{'before save'}} 오퍼레이션 후크에서 모델:{2}에 대해 {{id}} 특성을 {0}에서 {1}(으)로 변경할 수 없습니다.",
|
||||
"ac04cf275b71c1eb89a41cf6bbad7a64": "HasOne 메소드 \"getAsync()\"는 더 이상 사용되지 않습니다. 대신 \"get()\"을 사용하십시오.",
|
||||
"b138294f132edfe1eb2a8211150c7238": "조회에서 예상치 못한 `undefined` 항목",
|
||||
"b15b20280211ad258d92947f05b6e4a5": "커넥터가 초기화되지 않았습니다.",
|
||||
"b278876ec93ef9760f00e83f38ba313d": "Scope 메소드 \"getAsync()\"는 더 이상 사용되지 않습니다. 대신 \"find()\"를 사용하십시오.",
|
||||
"ba0fd8106eb54de4d003a844206431fd": "모델 후크 \"{0}\"이(가) 더 이상 사용되지 않습니다. 오퍼레이션 후크가 대신 사용됩니다. {{http://docs.strongloop.com/display/LB/Operation+hooks}}",
|
||||
"baf2c8b0c5a574b8a894e9b6304fece1": "where 절 {0}이(가) {{object}}이(가) 아님",
|
||||
"bdb11cc1c780c9ccac33c316cfdc9d82": "특성 {0}.{1}에 유형이 정의되지 않음",
|
||||
"bdfb951c8ff7ce0cbc08c06f548fd927": "값이 빈 {{object}}임",
|
||||
"bec226891a505828bfc76c5cfd73b336": "알 수 없는 키 {0}에 대한 TTL을 가져올 수 없음",
|
||||
"cd930369e86cdd222f7bd117c6f9fa94": "알 수 없는 기본값 제공자 {0}",
|
||||
"cfee4d8149316d9a647c0885cf3cafaf": "점이 포함된 특성 이름은 지원되지 않습니다. 모델: {0}, 동적 특성: {1}",
|
||||
"d40328eabd8756d795bcdd49d782d4e9": "데이터 소스에서 트랜잭션을 지원하지 않음",
|
||||
"da02dd6c53d4148320eeb31718a7aebe": "특성 {0}에 대한 올바르지 않은 유형",
|
||||
"da751a8a748adbde5b55fa83b707b4e2": "점이 포함된 특성 이름은 지원되지 않습니다. 모델: {0}, 특성: {1}",
|
||||
"db03083e9a768388fdbee865249ac67a": "{{updateOrCreate()}}에서 유효성 검증 오류 무시:",
|
||||
"dd63416d9b7d9fa4181e89efd619dfd8": "값이 순차 숫자 색인을 가진 {{array}} 또는 {{object}}이(가) 아님",
|
||||
"ddf0aa14803f1c84f4a97f3803f7471c": "클래스 이름 필수",
|
||||
"e08ab0e1ab55f26c357061447b635905": "{0}에서 ({1}.{2} ,{3}.{4})에 대한 관계를 찾을 수 없음",
|
||||
"e0e9504e137a3c3339144b51ed76fef2": "커넥터가 제대로 정의되지 않음: 데이터 소스의 `{{connector}}` 멤버를 작성해야 합니다.",
|
||||
"e2f282cbe3efba001d6d3a09f7f6ca8c": "{{polymorphic}} {0} 관계: `polymorphic.discriminator` 매개변수가 제공된 경우 {1}에는 `polymorphic.foreignKey` 매개변수가 필요함",
|
||||
"e39e0f5d52bfbf511e645d19ecadd2fa": "{0} 특성에 올바르지 않은 절 {1}이(가) 있음: {2}",
|
||||
"e4434de4bb8f5a3cd1d416e4d80d7e0b": "알 수 없는 \"{0}\" {{id}} \"{1}\".",
|
||||
"e54d944c2a2c85a23caa86027ae307cf": "이 데이터 소스에 첨부되지 않은 모델은 마이그레이션할 수 없음: {0}",
|
||||
"e54f118a75e15e132f16b985274eb46d": "조회 필터 {0}이(가) {{object}}가 아님",
|
||||
"e55937649d8d7a11706b8cec22d02eae": "{{HasOne}} 관계 {0}이(가) 비어 있음",
|
||||
"e6161ae8459c79d810e2aa9d21282a39": "속성을 업데이트하는 경우 {{id}}을(를) 제공해야 합니다!",
|
||||
"eb56c2b0c30cf006e2df00a549ec9c2c": "{1} 모델에 대해 관계 \"{0}\"이(가) 정의되지 않음",
|
||||
"ec42dca074f1818c447f7ad16e2d01af": "첨부된 커넥터에서 {0}을(를) 제공하지 않음",
|
||||
"ecb7aa804bf54c682999d20d6436104c": "{{transaction}}이(가) 활성 상태가 아님: {0}",
|
||||
"f30809cb932b72a66416a709c8531530": "커넥터가 트랜잭션 내에서 {{method}}을(를) 지원하지 않음",
|
||||
"f41bd91dc0f000a79c0bf842f1b7fdf9": "JSON 문자열에서 목록을 작성할 수 없음: {0}",
|
||||
"f6e8c96c93b9c7687d6c172b3695e898": "{{id}} 특성({0})을 {1}에서 {2}(으)로 업데이트할 수 없음",
|
||||
"fa9ae17e8e008d0eb0f0421a2972308c": "{{polymorphic}} {0} 관계: {1}에 `model` 매개변수가 필요함",
|
||||
"fca4d12faff1035d9d0438d73432571b": "{0}.{1}의 중복 항목",
|
||||
"fd3cc89dc67e2d604eaae21bdf41d403": "{1} 모델에 대해 {0} 관계를 찾을 수 없음",
|
||||
"fec8ebda24db46a9d040bf863765cc44": "{0} 연산자에 올바르지 않은 절 {1}이(가) 있음: {2}"
|
||||
}
|
||||
|
|
@ -0,0 +1,97 @@
|
|||
{
|
||||
"0483a77cf77741504204e5c066597487": "{{polymorphic}} {0} relatie: {1} verwacht niet dat de parameter `polymorphic.as` wordt gebruikt bij het definiëren van een aangepaste `foreignKey`/`discriminator` ",
|
||||
"09483e03b91c8bd58732a74b3ef1ec13": "Ongeldige datum: {0}",
|
||||
"0a5aa17f7866a85e3aee37ef5369403c": "LinkManyToMany heeft een doel ontvangen dat niet de vereiste \"{0}\" bevat",
|
||||
"0b16d3ffc42f91b4b9a4b3b50c41c838": "De volgorde {0} is niet geldig",
|
||||
"0bd753a8944ad0af85a939bb25273887": "Onbekende sleutel {0} kan niet vervallen",
|
||||
"0c0b867aca0973ba26e887d3337cc4ec": "Model {{Polymorphic}} is niet gevonden: '{0}' niet ingesteld",
|
||||
"0c4eb8b6c2ff6e51d7e195eee346ced9": "Tabel '{0}' bestaat niet.",
|
||||
"0ff31abb394afb555df162e74ff1a0a0": "{{id}} kan niet worden bijgewerkt van {0} naar {1} zolang {{forceId}} is ingesteld op true",
|
||||
"1ae7d3e0be381efb32bfd1ba652f5172": "WAARSCHUWING: {{polymorphic}} {0} relatie: {1} gebruikt trefwoord `polymorphic.as`. Dit wordt in LoopBack.next gedeprecieerd. Raadpleeg dit document voor vervangende oplossingen: (https://loopback.io/doc/en/lb3/Polymorphic-relations.html#deprecated-polymorphic-as)",
|
||||
"1daef4e937fe52136597ba8fd2060f55": "Het nesten van transacties wordt niet ondersteund",
|
||||
"21095484501dbff31af6556fa6039182": "De {{offset/skip}}-parameter {0} is niet geldig",
|
||||
"280f4550f90e133118955ec6f6f72830": "Discriminatortype {0} opgegeven, maar er bestaat geen model met deze naam",
|
||||
"28697ec15968a7969211f6d035ba9260": "{{polymorphic}} {0} relatie: {1} verwacht niet dat de parameter `model` wordt gebruikt",
|
||||
"2c4904377a87fdab502118719cc0d266": "{{Transaction}} wordt niet ondersteund",
|
||||
"2c5c8519721f749aab13c2f04f41d611": "De eigenschap {0} heeft een ongeldige clausule {1}: Er werden precies twee waarden verwacht. Ontvangen: {2}",
|
||||
"2f4af31c144bbfab1bbf479866acd820": "\nWaarschuwing: {{LoopBack}}-connector \"{0}\" is niet geïnstalleerd als een van de volgende modules:\n\n {1}\n\nU lost dit op door het uitvoeren van:\n\n {{npm install {2} --save}}\n",
|
||||
"3864f9be10f27723074566d2b3893514": "Waarschuwing: Model {0}, {{strict mode: `throw`}} is verwijderd. Gebruik in plaats hiervan {{`strict: true`}}; hiermee wordt {{`Validation Error`}} gemeld voor onbekende eigenschappen.",
|
||||
"38dbf42c29a4645238cc3d632e88ebc9": "{{Relation.modelTo}} is niet gedefinieerd voor relatie {0} en is geen {{polymorphic}}",
|
||||
"3cde8cc9bca22c67278b202ab0720106": "Geen instance met ID {0} gevonden voor {1}",
|
||||
"416dfbb7b823f51c9f3800be81060b41": "Geen instance met {{id}} {0} gevonden voor {1}",
|
||||
"49b5afd8c6a19ad9c8abeffb2f8114eb": "BelongsTo-methode \"getAsync()\" is gedeprecieerd. In plaats daarvan moet u \"get()\" gebruiken.",
|
||||
"4c78325cedbb826db3a05bf5df0e8546": "U moet een {{id}} opgeven bij een vervanging.",
|
||||
"4e31b1edd10dadb724d83387de0b5062": "De {{limit}}-parameter {0} is niet geldig",
|
||||
"514985b2327f061ffb1c932f6b909979": "Model {0} is niet gedefinieerd.",
|
||||
"525c856e65daab43be247e7b5410febd": "{{polymorphic}} {0} relatie: {1} verwacht niet dat de parameter `polymorphic.selector` wordt gebruikt bij het definiëren van een aangepaste `foreignKey`/`discriminator` ",
|
||||
"5c18ee111dd87540cdb19a2a93b33be9": "De transactie is teruggedraaid vanwege een timeout",
|
||||
"5ec7e6664256f7ea78f4f06dafc7d974": "De transactie is niet gereed, wacht tot de geretourneerde promise omgezet is",
|
||||
"5ec8efeb715a2c34b440f2d76e2cf87d": " {0}",
|
||||
"6111399276924ffa3bc9a410cdfcb2e5": "Geen {{id}}-naam {0}",
|
||||
"614e3355647e4127c96256102dc63376": "De eigenschap {0} heeft een ongeldige clausule {1}: Er werd een tekenreeks of expressie verwacht",
|
||||
"62a2d80c405b7fec5f547c448ab1b6ff": "De {{order}} {0} heeft een ongeldige richting",
|
||||
"6502a117987610380b9068ef98b1b0ee": "Geen record gevonden in {0} voor ({1}.{2} ,{3}.{4})",
|
||||
"67c2bf43b5281ab929617423ea8a6f3e": "De connector {0} biedt geen ondersteuning voor de bewerking {{replaceById}}. Dit is geen programmafout in LoopBack. Neem contact op met de auteurs van de connector, bij voorkeur via GitHub-problemen.",
|
||||
"6c3234937d69763fc7f6bcafccc59bbc": "Voor {{Model::deleteById}} is het argument {{id}} vereist",
|
||||
"6eb6fd4fbd73394000bc25f5776fd20c": "Voor {{Model::exists}} is het argument {{id}} vereist",
|
||||
"6fcc2ff0db7a4f490f5e0ce9e24691f3": "{{HasOne}}-relatie kan niet resulteren in meer dan één instance van {0}",
|
||||
"728232e473bf80272c042df2b7e002f4": "{{polymorphic}} {0} relatie: als de parameter `polymorphic.foreignKey` wordt opgegeven, vereist {1} de parameter `polymorphic.discriminator`",
|
||||
"791ab3031a73ede03f7d6299a85e8289": "Timeout voor het maken van verbinding na {0} ms",
|
||||
"7b277018e43d41bc445731092b91547d": "Geen verbinding",
|
||||
"7bbbdece4eea90e42aa5c0bce295e503": "Voor {{Model::findById}} is het argument {{id}} vereist",
|
||||
"7e9530c0399289be0ee601a604be71ff": "{{BelongsTo}}-relatie {0} is leeg",
|
||||
"7faa840eb6ce11250a141deb42a6c489": "Onbekende relatie {{scope}}: {0}",
|
||||
"8091838319a5cc7d6a34af2f2a616ce9": "Eigenschapnaam mag niet \"{{constructor}}\" zijn in model: {0}",
|
||||
"881e4b0cb86ed59549248ee540a9fd10": "Eigenschapnaam \"{{constructor}}\" is niet toegestaan in {0}-gegevens",
|
||||
"89afd3a9249f5a8d3edda07d84ca049d": "{{Polymorphic}}-model is niet gevonden: '{0}'",
|
||||
"89bf6d92731fe7bd2146ce8d0bec205c": "Ongeldig argument, moet een tekenreeks, {{regex}}-literaal of {{RegExp}}-object zijn",
|
||||
"8a39126103a157f501affa070367a1b0": "De instance {0} is niet geldig. Details: {1}.",
|
||||
"8c5ab01638c1ac1d58168c6346a8481a": "Ongeldige {{regex}}-vlaggen: {0}",
|
||||
"938401ea4ce48159efa9be1d4a5e8bab": "Items moeten een array zijn: {0}",
|
||||
"9e1f143ee02946324d34da92f71bf74e": "{0} relatie: {1} vereist de parameter `model`",
|
||||
"a004f310d315e592843776fab964eaeb": "{{Polymorphic}}-relaties hebben een doorvoermodel nodig",
|
||||
"a0cf0e09c26df14283223e84e6a10f00": "De kenmerken kunnen niet worden bijgewerkt. {{Object}} met {{id}} {0} bestaat niet!",
|
||||
"a2487abefef4259c2131d96cdb8543b1": "Verbinding mislukt: {0}\nVerbindingspoging wordt herhaald bij volgende opdracht.",
|
||||
"a25e41a39c60c4702e55d0c3936576a1": "Niet-overeenkomende sleutels: {0}.{1}: {2}, {3}.{4}: {5}",
|
||||
"a327355560d495454fba2c1aad6bdf09": "Onbekende methode voor bereik: {0}",
|
||||
"a6c18a7f4390cd3d59a2a7a047ae2aab": "Voer de opdracht \"{{npm install loopback-datasource-juggler}} {0}\" uit",
|
||||
"a829dee089c912e68c18920ba015400c": "Waarschuwing: Eigenschap {{id}} kan niet worden gewijzigd van {0} in {1} voor model:{2} in operation hook {{'loaded'}}",
|
||||
"a984a076c59e451948b2bcf7a393d860": "Waarschuwing: Eigenschap {{id}} kan niet worden gewijzigd van {0} in {1} voor model:{2} in operation hook {{'before save'}}",
|
||||
"ac04cf275b71c1eb89a41cf6bbad7a64": "HasOne-methode \"getAsync()\" is gedeprecieerd. In plaats daarvan moet u \"get()\" gebruiken.",
|
||||
"b138294f132edfe1eb2a8211150c7238": "Onverwacht item 'undefined' in query",
|
||||
"b15b20280211ad258d92947f05b6e4a5": "De connector is niet geïnitialiseerd.",
|
||||
"b278876ec93ef9760f00e83f38ba313d": "Scope-methode \"getAsync()\" is gedeprecieerd. In plaats daarvan moet u \"find()\" gebruiken.",
|
||||
"ba0fd8106eb54de4d003a844206431fd": "Model hook \"{0}\" is gedeprecieerd; gebruik in plaats daarvan operation hooks. {{http://docs.strongloop.com/display/LB/Operation+hooks}}",
|
||||
"baf2c8b0c5a574b8a894e9b6304fece1": "De WHERE-clausule {0} is geen {{object}}",
|
||||
"bdb11cc1c780c9ccac33c316cfdc9d82": "Type niet gedefinieerd voor eigenschap {0}.{1}",
|
||||
"bdfb951c8ff7ce0cbc08c06f548fd927": "Waarde is een leeg {{object}}",
|
||||
"bec226891a505828bfc76c5cfd73b336": "TTL voor onbekende sleutel {0} kan niet worden opgehaald",
|
||||
"cd930369e86cdd222f7bd117c6f9fa94": "Onbekende standaardwaarde voor provider {0}",
|
||||
"cfee4d8149316d9a647c0885cf3cafaf": "Eigenschapnamen met punt(en) worden niet ondersteund. Model: {0}, dynamische eigenschap: {1}",
|
||||
"d40328eabd8756d795bcdd49d782d4e9": "DataSource ondersteunt geen transacties",
|
||||
"da02dd6c53d4148320eeb31718a7aebe": "Ongeldig type voor eigenschap {0}",
|
||||
"da751a8a748adbde5b55fa83b707b4e2": "Eigenschapnamen met punt(en) worden niet ondersteund. Model: {0}, eigenschap: {1}",
|
||||
"db03083e9a768388fdbee865249ac67a": "Validatiefouten in {{updateOrCreate()}} worden genegeerd:",
|
||||
"dd63416d9b7d9fa4181e89efd619dfd8": "Waarde is geen {{array}} of {{object}} met sequentiële numerieke indices",
|
||||
"ddf0aa14803f1c84f4a97f3803f7471c": "Klassennaam vereist",
|
||||
"e08ab0e1ab55f26c357061447b635905": "Geen relatie gevonden in in {0} voor ({1}.{2},{3}.{4})",
|
||||
"e0e9504e137a3c3339144b51ed76fef2": "Connector is niet juist gedefinieerd: moet '{{connector}}'-lid van gegevensbron maken",
|
||||
"e2f282cbe3efba001d6d3a09f7f6ca8c": "{{polymorphic}} {0} relatie: als de parameter `polymorphic.discriminator` wordt opgegeven, vereist {1} de parameter `polymorphic.foreignKey`",
|
||||
"e39e0f5d52bfbf511e645d19ecadd2fa": "De eigenschap {0} heeft een ongeldige clausule {1}: {2}",
|
||||
"e4434de4bb8f5a3cd1d416e4d80d7e0b": "Onbekend \"{0}\" {{id}} \"{1}\".",
|
||||
"e54d944c2a2c85a23caa86027ae307cf": "Kan geen modellen migreren die niet zijn gekoppeld aan deze gegevensbron: {0}",
|
||||
"e54f118a75e15e132f16b985274eb46d": "Het queryfilter {0} is geen {{object}}",
|
||||
"e55937649d8d7a11706b8cec22d02eae": "{{HasOne}}-relatie {0} is leeg",
|
||||
"e6161ae8459c79d810e2aa9d21282a39": "U moet een {{id}} opgeven bij het bijwerken van kenmerken.",
|
||||
"eb56c2b0c30cf006e2df00a549ec9c2c": "Relatie \"{0}\" is niet gedefinieerd voor model {1}",
|
||||
"ec42dca074f1818c447f7ad16e2d01af": "{0} is niet opgegeven door gekoppelde connector",
|
||||
"ecb7aa804bf54c682999d20d6436104c": "De {{transaction}} is niet actief: {0}",
|
||||
"f30809cb932b72a66416a709c8531530": "De connector ondersteunt {{method}} niet binnen een transactie",
|
||||
"f41bd91dc0f000a79c0bf842f1b7fdf9": "kan geen lijst maken op basis van JSON-reeks: {0}",
|
||||
"f6e8c96c93b9c7687d6c172b3695e898": "{{id}}-eigenschap ({0}) kan niet worden bijgewerkt van {1} in {2}",
|
||||
"fa9ae17e8e008d0eb0f0421a2972308c": "{{polymorphic}} {0} relatie: {1} vereist de parameter `model`",
|
||||
"fca4d12faff1035d9d0438d73432571b": "Dubbel item voor {0}.{1}",
|
||||
"fd3cc89dc67e2d604eaae21bdf41d403": "Relatie {0} voor model {1} is niet gevonden",
|
||||
"fec8ebda24db46a9d040bf863765cc44": "De operator {0} heeft ongeldige clausules {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": "Relação {{polymorphic}} de {0}: {1} não espera o parâmetro 'polymorphic.as' ao definir 'foreignKey'/'discriminator' customizado ",
|
||||
"09483e03b91c8bd58732a74b3ef1ec13": "Data inválida: {0}",
|
||||
"0a5aa17f7866a85e3aee37ef5369403c": "LinkManyToMany recebeu destino que não contém \"{0}\" necessário",
|
||||
"0b16d3ffc42f91b4b9a4b3b50c41c838": "A ordem {0} não é válida",
|
||||
"0bd753a8944ad0af85a939bb25273887": "Não é possível expirar chave desconhecida {0}",
|
||||
"0c0b867aca0973ba26e887d3337cc4ec": "Modelo de {{Polymorphic}} não localizado: `{0}` não configurado",
|
||||
"0c4eb8b6c2ff6e51d7e195eee346ced9": "A tabela '{0}' não existe.",
|
||||
"0ff31abb394afb555df162e74ff1a0a0": "{{id}} não pode ser atualizado de {0} para {1} quando {{forceId}} é configurado como true",
|
||||
"1ae7d3e0be381efb32bfd1ba652f5172": "AVISO: relação {{polymorphic}} de {0}: {1} usa a palavra-chave 'polymorphic.as', que será DESCONTINUADA em LoopBack.next; consulte este doc para obter soluções de substituição (https://loopback.io/doc/en/lb3/Polymorphic-relations.html#deprecated-polymorphic-as)",
|
||||
"1daef4e937fe52136597ba8fd2060f55": "Transações aninhadas não são suportadas",
|
||||
"21095484501dbff31af6556fa6039182": "O parâmetro {{offset/skip}} {0} não é válido",
|
||||
"280f4550f90e133118955ec6f6f72830": "Tipo de discriminador {0} especificado, mas não existe nenhum modelo com esse nome",
|
||||
"28697ec15968a7969211f6d035ba9260": "Relação {{polymorphic}} de {0}: {1} não espera o parâmetro 'model'",
|
||||
"2c4904377a87fdab502118719cc0d266": "{{Transaction}} não é suportada",
|
||||
"2c5c8519721f749aab13c2f04f41d611": "A propriedade {0} possui cláusula inválida {1}: esperado exatamente 2 valores, recebidos {2}",
|
||||
"2f4af31c144bbfab1bbf479866acd820": "\nAVISO: conector {{LoopBack}} \"{0}\" não foi instalado como qualquer um dos módulos a seguir:\n\n {1}\n\nPara corrigir, execute:\n\n {{npm install {2} --save}}\n",
|
||||
"3864f9be10f27723074566d2b3893514": "Aviso: o modelo {0}, {{strict mode: `throw`}} foi removido, use {{`strict: true`}} no lugar, que retorna {{`Validation Error`}} para as propriedades desconhecidas,",
|
||||
"38dbf42c29a4645238cc3d632e88ebc9": "{{Relation.modelTo}} não foi definido para a relação {0} e não é {{polymorphic}}",
|
||||
"3cde8cc9bca22c67278b202ab0720106": "Nenhuma instância com ID {0} localizada para {1}",
|
||||
"416dfbb7b823f51c9f3800be81060b41": "Nenhuma instância com {{id}} {0} localizada para {1}",
|
||||
"49b5afd8c6a19ad9c8abeffb2f8114eb": "O método BelongsTo \"getAsync()\" está descontinuado, use \"get()\" em vez disso.",
|
||||
"4c78325cedbb826db3a05bf5df0e8546": "Deve-se fornecer um {{id}} ao substituir!",
|
||||
"4e31b1edd10dadb724d83387de0b5062": "O parâmetro {0} de {{limit}} não é válido",
|
||||
"514985b2327f061ffb1c932f6b909979": "O modelo {0} não está definido.",
|
||||
"525c856e65daab43be247e7b5410febd": "Relação {{polymorphic}} de {0}: {1} não espera o parâmetro 'polymorphic.selector' ao definir 'foreignKey'/'discriminator' customizado ",
|
||||
"5c18ee111dd87540cdb19a2a93b33be9": "A transação é recuperada devido à transcorrência do tempo limite",
|
||||
"5ec7e6664256f7ea78f4f06dafc7d974": "A transação não está pronta, aguarde a promessa retornada para resolver",
|
||||
"5ec8efeb715a2c34b440f2d76e2cf87d": " {0}",
|
||||
"6111399276924ffa3bc9a410cdfcb2e5": "Nenhum nome de {{id}} {0}",
|
||||
"614e3355647e4127c96256102dc63376": "A propriedade {0} possui cláusula inválida {1}: esperada uma sequência ou um RegExp",
|
||||
"62a2d80c405b7fec5f547c448ab1b6ff": "A {{order}} {0} possui direção inválida",
|
||||
"6502a117987610380b9068ef98b1b0ee": "Nenhum registro encontrado em {0} para ({1}.{2} ,{3}.{4})",
|
||||
"67c2bf43b5281ab929617423ea8a6f3e": "O conector {0} não suporta operação {{replaceById}}. Este não é um erro no LoopBack. Entre em contato com os autores do conector, de preferência via problemas do GitHub.",
|
||||
"6c3234937d69763fc7f6bcafccc59bbc": "{{Model::deleteById}} requer o argumento {{id}}",
|
||||
"6eb6fd4fbd73394000bc25f5776fd20c": "{{Model::exists}} requer o argumento {{id}}",
|
||||
"6fcc2ff0db7a4f490f5e0ce9e24691f3": "A relação {{HasOne}} não pode criar mais de uma instância de {0}",
|
||||
"728232e473bf80272c042df2b7e002f4": "Relação {{polymorphic}} de {0}: {1} requer o parâmetro 'polymorphic.discriminator' quando o parâmetro 'polymorphic.foreignKey' é fornecido",
|
||||
"791ab3031a73ede03f7d6299a85e8289": "Tempo limite na conexão após {0} ms",
|
||||
"7b277018e43d41bc445731092b91547d": "Não está conectado",
|
||||
"7bbbdece4eea90e42aa5c0bce295e503": "{{Model::findById}} requer o argumento {{id}}",
|
||||
"7e9530c0399289be0ee601a604be71ff": "Relação {{BelongsTo}} {0} está vazia",
|
||||
"7faa840eb6ce11250a141deb42a6c489": "{{scope}} da relação desconhecido: {0}",
|
||||
"8091838319a5cc7d6a34af2f2a616ce9": "O nome da propriedade não deve ser \"{{constructor}}\" no Modelo: {0}",
|
||||
"881e4b0cb86ed59549248ee540a9fd10": "O nome da propriedade \"{{constructor}}\" não é permitido nos dados de {0}",
|
||||
"89afd3a9249f5a8d3edda07d84ca049d": "Modelo de {{Polymorphic}} não localizado: `{0}`",
|
||||
"89bf6d92731fe7bd2146ce8d0bec205c": "Argumento inválido, deve ser uma sequência, literal {{regex}} ou objeto {{RegExp}}",
|
||||
"8a39126103a157f501affa070367a1b0": "A instância de {0} não é válida. Detalhes: {1}.",
|
||||
"8c5ab01638c1ac1d58168c6346a8481a": "Sinalizações de {{regex}} inválidas: {0}",
|
||||
"938401ea4ce48159efa9be1d4a5e8bab": "Itens devem ser uma matriz: {0}",
|
||||
"9e1f143ee02946324d34da92f71bf74e": "Relação de {0}: {1} requer o parâmetro 'model'",
|
||||
"a004f310d315e592843776fab964eaeb": "Relações de {{Polymorphic}} precisam de um modelo completo",
|
||||
"a0cf0e09c26df14283223e84e6a10f00": "Não foi possível atualizar atributos. {{Object}} com {{id}} {0} não existe!",
|
||||
"a2487abefef4259c2131d96cdb8543b1": "Falha de conexão: {0}\nEla será tentada novamente para a próxima solicitação.",
|
||||
"a25e41a39c60c4702e55d0c3936576a1": "Incompatibilidade de chaves: {0}.{1}: {2}, {3}.{4}: {5}",
|
||||
"a327355560d495454fba2c1aad6bdf09": "Método de escopo desconhecido: {0}",
|
||||
"a6c18a7f4390cd3d59a2a7a047ae2aab": "Execute o comando \"{{npm install loopback-datasource-juggler}} {0}\" ",
|
||||
"a829dee089c912e68c18920ba015400c": "AVISO: a propriedade {{id}} não pode ser mudada de {0} para {1} para o modelo:{2} no gancho de operação {{'loaded'}}",
|
||||
"a984a076c59e451948b2bcf7a393d860": "AVISO: a propriedade {{id}} não pode ser mudada de {0} para {1} para o modelo:{2} no gancho de operação {{'before save'}}",
|
||||
"ac04cf275b71c1eb89a41cf6bbad7a64": "O método HasOne \"getAsync()\" está descontinuado, use \"get()\" em vez disso.",
|
||||
"b138294f132edfe1eb2a8211150c7238": "`Indefinido` inesperado na consulta",
|
||||
"b15b20280211ad258d92947f05b6e4a5": "O conector não foi inicializado.",
|
||||
"b278876ec93ef9760f00e83f38ba313d": "O método Scope \"getAsync()\" está descontinuado, use \"find()\" em vez disso.",
|
||||
"ba0fd8106eb54de4d003a844206431fd": "O gancho de modelo \"{0}\" está descontinuado, use ganchos de Operação no lugar. {{http://docs.strongloop.com/display/LB/Operation+hooks}}",
|
||||
"baf2c8b0c5a574b8a894e9b6304fece1": "A cláusula where {0} não é um {{object}}",
|
||||
"bdb11cc1c780c9ccac33c316cfdc9d82": "Tipo não definido para a propriedade {0}.{1}",
|
||||
"bdfb951c8ff7ce0cbc08c06f548fd927": "O valor é um {{object}} vazio",
|
||||
"bec226891a505828bfc76c5cfd73b336": "Não é possível obter TTL para chave desconhecida {0}",
|
||||
"cd930369e86cdd222f7bd117c6f9fa94": "Provedor do valor padrão {0} desconhecido",
|
||||
"cfee4d8149316d9a647c0885cf3cafaf": "Nomes da propriedade contendo pontos não são suportados. Modelo: {0}, propriedade dinâmica: {1}",
|
||||
"d40328eabd8756d795bcdd49d782d4e9": "A Origem de Dados não suporta transações",
|
||||
"da02dd6c53d4148320eeb31718a7aebe": "Tipo inválido para a propriedade {0}",
|
||||
"da751a8a748adbde5b55fa83b707b4e2": "Nomes da propriedade contendo pontos não são suportados. Modelo: {0}, propriedade: {1}",
|
||||
"db03083e9a768388fdbee865249ac67a": "Ignorando erros de validação em {{updateOrCreate()}}:",
|
||||
"dd63416d9b7d9fa4181e89efd619dfd8": "O valor não é um {{array}} ou {{object}} com índices numéricos sequenciais",
|
||||
"ddf0aa14803f1c84f4a97f3803f7471c": "Nome de classe necessário",
|
||||
"e08ab0e1ab55f26c357061447b635905": "Nenhuma relação encontrada em {0} para ({1}.{2},{3}.{4})",
|
||||
"e0e9504e137a3c3339144b51ed76fef2": "O conector não foi definido corretamente: ele deve criar um membro de `{{connector}}` igual a dataSource",
|
||||
"e2f282cbe3efba001d6d3a09f7f6ca8c": "Relação {{polymorphic}} de {0}: {1} requer o parâmetro 'polymorphic.foreignKey' quando o parâmetro 'polymorphic.discriminator' é fornecido",
|
||||
"e39e0f5d52bfbf511e645d19ecadd2fa": "A propriedade {0} possui cláusula inválida {1}: {2}",
|
||||
"e4434de4bb8f5a3cd1d416e4d80d7e0b": "\"{0}\" {{id}} \"{1}\" desconhecido.",
|
||||
"e54d944c2a2c85a23caa86027ae307cf": "Não é possível migrar modelos não conectados a esta origem de dados: {0}",
|
||||
"e54f118a75e15e132f16b985274eb46d": "O filtro de consulta {0} não é um {{object}}",
|
||||
"e55937649d8d7a11706b8cec22d02eae": "Relação {{HasOne}} {0} está vazia",
|
||||
"e6161ae8459c79d810e2aa9d21282a39": "Deve-se fornecer um {{id}} ao atualizar atributos!",
|
||||
"eb56c2b0c30cf006e2df00a549ec9c2c": "A relação \"{0}\" não foi definida para o modelo {1}",
|
||||
"ec42dca074f1818c447f7ad16e2d01af": "{0} não é fornecido pelo conector conectado",
|
||||
"ecb7aa804bf54c682999d20d6436104c": "A {{transaction}} não está ativa: {0}",
|
||||
"f30809cb932b72a66416a709c8531530": "O conector não suporta {{method}} dentro de uma transação",
|
||||
"f41bd91dc0f000a79c0bf842f1b7fdf9": "não foi possível criar Lista a partir da sequência JSON: {0}",
|
||||
"f6e8c96c93b9c7687d6c172b3695e898": "A propriedade de {{id}} ({0}) não pode ser atualizada de {1} para {2}",
|
||||
"fa9ae17e8e008d0eb0f0421a2972308c": "Relação {{polymorphic}} de {0}: {1} requer o parâmetro 'model'",
|
||||
"fca4d12faff1035d9d0438d73432571b": "Entrada suplicada para {0}.{1}",
|
||||
"fd3cc89dc67e2d604eaae21bdf41d403": "Não foi possível localizar a relação {0} para o modelo {1}",
|
||||
"fec8ebda24db46a9d040bf863765cc44": "O operador {0} possui cláusulas inválidas {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}"
|
||||
}
|
||||
|
|
@ -0,0 +1,97 @@
|
|||
{
|
||||
"0483a77cf77741504204e5c066597487": "{{polymorphic}} {0} ilişkisi: {1}, özel `foreignKey`/`discriminator` tanımlarken `polymorphic.as` parametresini beklemiyor ",
|
||||
"09483e03b91c8bd58732a74b3ef1ec13": "Geçersiz tarih: {0}",
|
||||
"0a5aa17f7866a85e3aee37ef5369403c": "LinkManyToMany, gerekli \"{0}\" öğesini içermeyen hedef aldı",
|
||||
"0b16d3ffc42f91b4b9a4b3b50c41c838": "{0} sırası geçerli değil",
|
||||
"0bd753a8944ad0af85a939bb25273887": "Bilinmeyen {0} anahtarı süre bitimine uğratılamaz",
|
||||
"0c0b867aca0973ba26e887d3337cc4ec": "{{Polymorphic}} model bulunamadı: `{0}` ayarlı değil",
|
||||
"0c4eb8b6c2ff6e51d7e195eee346ced9": "'{0}' tablosu yok",
|
||||
"0ff31abb394afb555df162e74ff1a0a0": "{{id}}, {{forceId}} true olarak ayarlandığında {0} durumundan {1} durumuna güncellenemiyor",
|
||||
"1ae7d3e0be381efb32bfd1ba652f5172": "UYARI: {{polymorphic}} {0} ilişkisi: {1}, LoopBack.next içinde KULLANIMDAN KALDIRILACAK `polymorphic.as` anahtar sözcüğünü kullanıyor; değiştirme çözümleri için bu belgeye bakın (https://loopback.io/doc/en/lb3/Polymorphic-relations.html#deprecated-polymorphic-as)",
|
||||
"1daef4e937fe52136597ba8fd2060f55": "İç içe geçmiş işlemler desteklenmiyor",
|
||||
"21095484501dbff31af6556fa6039182": "{{offset/skip}} parametresi {0} geçerli değil",
|
||||
"280f4550f90e133118955ec6f6f72830": "{0} ayrımsayıcı tipi belirtildi, ancak bu adı taşıyan bir model yok",
|
||||
"28697ec15968a7969211f6d035ba9260": "{{polymorphic}} {0} ilişkisi: {1}, `model` parametresini beklemiyor",
|
||||
"2c4904377a87fdab502118719cc0d266": "{{Transaction}} desteklenmiyor",
|
||||
"2c5c8519721f749aab13c2f04f41d611": "{0} özelliği geçersiz {1} yantümcesi içeriyor: Tam olarak 2 değer bekleniyor, {2} alındı",
|
||||
"2f4af31c144bbfab1bbf479866acd820": "\nUYARI: {{LoopBack}} bağlayıcısı \"{0}\" şu modüllerden biri olarak kurulmadı:\n\n {1}\n\nDüzeltmek için şu komutu çalıştırın:\n\n {{npm install {2} --save}}\n",
|
||||
"3864f9be10f27723074566d2b3893514": "Uyarı: {0} modeli, {{strict mode: `throw`}} kaldırıldı, lütfen bunun yerine bilinmeyen özellikler için {{`Validation Error`}} döndüren {{`strict: true`}} kullanın",
|
||||
"38dbf42c29a4645238cc3d632e88ebc9": "{{Relation.modelTo}}, {0} ilişkisi için tanımlanmadı ve {{polymorphic}} değil",
|
||||
"3cde8cc9bca22c67278b202ab0720106": "{1} için {0} tanıtıcılı bir eşgörünüm bulunamadı",
|
||||
"416dfbb7b823f51c9f3800be81060b41": "{1} için {{id}} {0} tanıtıcılı bir eşgörünüm bulunamadı",
|
||||
"49b5afd8c6a19ad9c8abeffb2f8114eb": "\"getAsync()\" BelongsTo yöntemi kullanımdan kaldırıldı, bunun yerine \"get()\" kullanın.",
|
||||
"4c78325cedbb826db3a05bf5df0e8546": "Değiştirirken bir {{id}} belirtmelisiniz!",
|
||||
"4e31b1edd10dadb724d83387de0b5062": "{{limit}} parametresi {0} geçerli değil",
|
||||
"514985b2327f061ffb1c932f6b909979": "{0} modeli tanımlanmadı",
|
||||
"525c856e65daab43be247e7b5410febd": "{{polymorphic}} {0} ilişkisi: {1}, özel `foreignKey`/`discriminator` öğesini tanımlarken `polymorphic.selector` parametresini beklemiyor ",
|
||||
"5c18ee111dd87540cdb19a2a93b33be9": "Zaman aşımı nedeniyle işlem geri alındı",
|
||||
"5ec7e6664256f7ea78f4f06dafc7d974": "İşlem hazır değil, çözüm için döndürülen durumu bekleyin. ",
|
||||
"5ec8efeb715a2c34b440f2d76e2cf87d": " {0}",
|
||||
"6111399276924ffa3bc9a410cdfcb2e5": "{{id}} adı {0} yok",
|
||||
"614e3355647e4127c96256102dc63376": "{0} özelliğinde geçersiz {1} yantümcesi var: Bir dizgi ya da RegExp bekleniyor",
|
||||
"62a2d80c405b7fec5f547c448ab1b6ff": "{{order}} {0} sırasının yönü geçersiz",
|
||||
"6502a117987610380b9068ef98b1b0ee": "({1}.{2} ,{3}.{4}) için {0} içinde kayıt bulunamadı. ",
|
||||
"67c2bf43b5281ab929617423ea8a6f3e": "{0} bağlayıcısı {{replaceById}} işlemini desteklemiyor. Bu LoopBack'deki bir hata değil. Lütfen, mümkünse GitHub sorunlarıyla birlikte, bağlayıcının yazarlarına başvurun.",
|
||||
"6c3234937d69763fc7f6bcafccc59bbc": "{{Model::deleteById}}, {{id}} bağımsız değişkenini gerektirir",
|
||||
"6eb6fd4fbd73394000bc25f5776fd20c": "{{Model::exists}}, {{id}} bağımsız değişkenini gerektirir",
|
||||
"6fcc2ff0db7a4f490f5e0ce9e24691f3": "{{HasOne}} ilişkisi birden çok {0} eşgörünümü yaratamaz",
|
||||
"728232e473bf80272c042df2b7e002f4": "{{polymorphic}} {0} ilişkisi: `polymorphic.foreignKey` parametresi sağlandığında {1} için `polymorphic.discriminator` parametresi gerekir",
|
||||
"791ab3031a73ede03f7d6299a85e8289": "{0} milisaniyeden sonra bağlantıda zaman aşımı oluştu",
|
||||
"7b277018e43d41bc445731092b91547d": "Bağlı değil",
|
||||
"7bbbdece4eea90e42aa5c0bce295e503": "{{Model::findById}}, {{id}} bağımsız değişkenini gerektirir",
|
||||
"7e9530c0399289be0ee601a604be71ff": "{{BelongsTo}} ilişkisi {0} boş",
|
||||
"7faa840eb6ce11250a141deb42a6c489": "Bilinmeyen ilişki {{scope}}: {0}",
|
||||
"8091838319a5cc7d6a34af2f2a616ce9": "Şu modelde özellik adı \"{{constructor}}\" olmamalıdır: {0}",
|
||||
"881e4b0cb86ed59549248ee540a9fd10": "{0} verilerinde \"{{constructor}}\" özellik adına izin verilmez",
|
||||
"89afd3a9249f5a8d3edda07d84ca049d": "{{Polymorphic}} model bulunamadı: `{0}`",
|
||||
"89bf6d92731fe7bd2146ce8d0bec205c": "Bağımsız değişken geçersiz; bir dizgi, {{regex}} hazır bilgisi ya da {{RegExp}} nesnesi olmalıdır",
|
||||
"8a39126103a157f501affa070367a1b0": "{0} eşgörünümü geçerli değil. Ayrıntılar: {1}.",
|
||||
"8c5ab01638c1ac1d58168c6346a8481a": "Geçersiz {{regex}} işaretleri: {0}",
|
||||
"938401ea4ce48159efa9be1d4a5e8bab": "Öğeler bir dizi olmalıdır: {0}",
|
||||
"9e1f143ee02946324d34da92f71bf74e": "{0} ilişkisi: {1} için `model` parametresi gerekir",
|
||||
"a004f310d315e592843776fab964eaeb": "{{Polymorphic}} ilişkileri için aracı model gerekir",
|
||||
"a0cf0e09c26df14283223e84e6a10f00": "Öznitelikler güncellenemedi. Tanıtıcısı {{id}} olan {0} {{Object}} nesnesi yok!",
|
||||
"a2487abefef4259c2131d96cdb8543b1": "Bağlantı başarısız oldu: {0}\nSonraki istek için yeniden denenecek.",
|
||||
"a25e41a39c60c4702e55d0c3936576a1": "Anahtar uyuşmazlığı: {0}.{1}: {2}, {3}.{4}: {5}",
|
||||
"a327355560d495454fba2c1aad6bdf09": "Bilinmeyen kapsam yöntemi: {0}",
|
||||
"a6c18a7f4390cd3d59a2a7a047ae2aab": "\"{{npm install loopback-datasource-juggler}} {0}\" komutunu çalıştırın ",
|
||||
"a829dee089c912e68c18920ba015400c": "UYARI: {{id}} özelliği, {{'loaded'}} işlem çengelinde {2} modeli için {0} değerinden {1} değerine çevrilemiyor",
|
||||
"a984a076c59e451948b2bcf7a393d860": "UYARI: {{id}} özelliği, {{'before save'}} işlem çengelinde {2} modeli için {0} değerinden {1} değerine çevrilemiyor",
|
||||
"ac04cf275b71c1eb89a41cf6bbad7a64": "\"getAsync()\" HasOne yöntemi kullanımdan kaldırıldı, bunun yerine \"get()\" kullanın",
|
||||
"b138294f132edfe1eb2a8211150c7238": "Sorguda beklenmeyen `undefined`",
|
||||
"b15b20280211ad258d92947f05b6e4a5": "Bağlayıcı başlatılmamış.",
|
||||
"b278876ec93ef9760f00e83f38ba313d": "\"getAsync()\" kapsam yöntemi kullanımdan kaldırıldı, bunun yerine \"find()\" kullanın.",
|
||||
"ba0fd8106eb54de4d003a844206431fd": "\"{0}\" model çengeli kullanımdan kaldırıldı, onun yerine işlem çengellerini kullanın. {{http://docs.strongloop.com/display/LB/Operation+hooks}}",
|
||||
"baf2c8b0c5a574b8a894e9b6304fece1": "Where yantümcesi {0} bir {{object}} değil",
|
||||
"bdb11cc1c780c9ccac33c316cfdc9d82": "{0}.{1} özelliği için tip tanımlanmadı",
|
||||
"bdfb951c8ff7ce0cbc08c06f548fd927": "Değer boş bir {{object}}",
|
||||
"bec226891a505828bfc76c5cfd73b336": "Bilinmeyen {0} anahtarı için TTL alınamıyor",
|
||||
"cd930369e86cdd222f7bd117c6f9fa94": "Bilinmeyen varsayılan değer sağlayıcısı {0}",
|
||||
"cfee4d8149316d9a647c0885cf3cafaf": "Nokta içeren özellik adları desteklenmez. Model: {0}, dinamik özellik: {1}",
|
||||
"d40328eabd8756d795bcdd49d782d4e9": "DataSource işlemleri desteklemiyor",
|
||||
"da02dd6c53d4148320eeb31718a7aebe": "{0} özelliği için geçersiz tip",
|
||||
"da751a8a748adbde5b55fa83b707b4e2": "Nokta içeren özellik adları desteklenmez. Model: {0}, özellik: {1}",
|
||||
"db03083e9a768388fdbee865249ac67a": "{{updateOrCreate()}} işlemindeki doğrulama hataları yoksayılıyor:",
|
||||
"dd63416d9b7d9fa4181e89efd619dfd8": "Değer, sıralı sayısal dizeler içeren bir {{array}} ya da {{object}} değil",
|
||||
"ddf0aa14803f1c84f4a97f3803f7471c": "Sınıf adı zorunludur",
|
||||
"e08ab0e1ab55f26c357061447b635905": "({1}.{2} ,{3}.{4}) için {0} içinde ilişki bulunamadı.",
|
||||
"e0e9504e137a3c3339144b51ed76fef2": "Bağlayıcı doğru tanımlanmadı: Veri kaynağının `{{connector}}` üyesini yaratmalıdır",
|
||||
"e2f282cbe3efba001d6d3a09f7f6ca8c": "{{polymorphic}} {0} ilişkisi: `polymorphic.discriminator` parametresi sağlandığında {1} için `polymorphic.foreignKey` parametresi gerekir",
|
||||
"e39e0f5d52bfbf511e645d19ecadd2fa": "{0} özelliğinde geçersiz {1} yantümcesi var: {2}",
|
||||
"e4434de4bb8f5a3cd1d416e4d80d7e0b": "Bilinmeyen \"{0}\" {{id}} \"{1}\".",
|
||||
"e54d944c2a2c85a23caa86027ae307cf": "Bu veri kaynağına eklenmemiş modeller geçirilemez: {0}",
|
||||
"e54f118a75e15e132f16b985274eb46d": "Sorgu süzgeci {0} bir {{object}} değil",
|
||||
"e55937649d8d7a11706b8cec22d02eae": "{{HasOne}} ilişkisi {0} boş",
|
||||
"e6161ae8459c79d810e2aa9d21282a39": "Öznitelikleri güncellerken bir {{id}} belirtmelisiniz!",
|
||||
"eb56c2b0c30cf006e2df00a549ec9c2c": "{1} modeli için \"{0}\" ilişkisi tanımlanmadı",
|
||||
"ec42dca074f1818c447f7ad16e2d01af": "Eklenen bağlayıcı tarafından {0} sağlanmadı",
|
||||
"ecb7aa804bf54c682999d20d6436104c": "{{transaction}} etkin değil: {0}",
|
||||
"f30809cb932b72a66416a709c8531530": "Bağlayıcısı bir işlem içinde {{method}} yöntemini desteklemiyor.",
|
||||
"f41bd91dc0f000a79c0bf842f1b7fdf9": "JSON dizgisinden liste yaratılamadı: {0}",
|
||||
"f6e8c96c93b9c7687d6c172b3695e898": "{{id}} özelliği ({0}), {1} değerinden {2} değerine güncellenemiyor",
|
||||
"fa9ae17e8e008d0eb0f0421a2972308c": "{{polymorphic}} {0} ilişkisi: {1} için `model` parametresi gerekir",
|
||||
"fca4d12faff1035d9d0438d73432571b": "{0}.{1} için yinelenen giriş",
|
||||
"fd3cc89dc67e2d604eaae21bdf41d403": "{1} modeli için {0} ilişkisi bulunamadı",
|
||||
"fec8ebda24db46a9d040bf863765cc44": "{0} işlecinde geçersiz {1} yantümceleri var: {2}"
|
||||
}
|
||||
|
|
@ -0,0 +1,97 @@
|
|||
{
|
||||
"0483a77cf77741504204e5c066597487": "{{polymorphic}} {0} 关系:定义定制 `foreignKey`/`discriminator` 时,{1} 不期望参数 `polymorphic.as`",
|
||||
"09483e03b91c8bd58732a74b3ef1ec13": "无效日期:{0}",
|
||||
"0a5aa17f7866a85e3aee37ef5369403c": "LinkManyToMany 收到的目标不包含必需的“{0}”",
|
||||
"0b16d3ffc42f91b4b9a4b3b50c41c838": "顺序 {0} 无效",
|
||||
"0bd753a8944ad0af85a939bb25273887": "无法使未知键 {0} 到期",
|
||||
"0c0b867aca0973ba26e887d3337cc4ec": "找不到 {{Polymorphic}} 模型:未设置“{0}”",
|
||||
"0c4eb8b6c2ff6e51d7e195eee346ced9": "表“{0}”不存在。",
|
||||
"0ff31abb394afb555df162e74ff1a0a0": "将 {{forceId}} 设置为 true 时,{{id}} 无法从 {0} 更新为 {1}",
|
||||
"1ae7d3e0be381efb32bfd1ba652f5172": "警告:{{polymorphic}} {0} 关系:{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": "{{polymorphic}} {0} 关系:{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": "未针对关系 {0} 定义 {{Relation.modelTo}},并且不是 {{polymorphic}}",
|
||||
"3cde8cc9bca22c67278b202ab0720106": "对于 {1},找不到具有标识 {0} 的实例",
|
||||
"416dfbb7b823f51c9f3800be81060b41": "对于 {1},找不到具有 {{id}} {0} 的实例",
|
||||
"49b5afd8c6a19ad9c8abeffb2f8114eb": "不推荐使用 BelongsTo 方法“getAsync()”,请改为使用“get()”。",
|
||||
"4c78325cedbb826db3a05bf5df0e8546": "在替换时必须提供 {{id}}!",
|
||||
"4e31b1edd10dadb724d83387de0b5062": "{{limit}} 参数 {0} 无效",
|
||||
"514985b2327f061ffb1c932f6b909979": "未定义模型 {0}。",
|
||||
"525c856e65daab43be247e7b5410febd": "{{polymorphic}} {0} 关系:定义定制 `foreignKey`/`discriminator` 时,{1} 不期望参数 `polymorphic.selector`",
|
||||
"5c18ee111dd87540cdb19a2a93b33be9": "事务因超时而回滚",
|
||||
"5ec7e6664256f7ea78f4f06dafc7d974": "事务未就绪,请等待返回的约定解决",
|
||||
"5ec8efeb715a2c34b440f2d76e2cf87d": " {0}",
|
||||
"6111399276924ffa3bc9a410cdfcb2e5": "无 {{id}} 名称 {0}",
|
||||
"614e3355647e4127c96256102dc63376": "{0} 属性具有无效子句 {1}:预期为字符串或 RegExp",
|
||||
"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": "{{polymorphic}} {0} 关系:提供参数 `polymorphic.foreignKey` 时,{1} 需要参数 `polymorphic.discriminator`",
|
||||
"791ab3031a73ede03f7d6299a85e8289": "在 {0} 毫秒后连接超时",
|
||||
"7b277018e43d41bc445731092b91547d": "未连接",
|
||||
"7bbbdece4eea90e42aa5c0bce295e503": "{{Model::findById}} 需要 {{id}} 自变量",
|
||||
"7e9530c0399289be0ee601a604be71ff": "{{BelongsTo}} 关系 {0} 为空",
|
||||
"7faa840eb6ce11250a141deb42a6c489": "未知的关系 {{scope}}:{0}",
|
||||
"8091838319a5cc7d6a34af2f2a616ce9": "属性名称不应是模型中的“{{constructor}}”:{0}",
|
||||
"881e4b0cb86ed59549248ee540a9fd10": "{0} 数据中不允许属性名称“{{constructor}}”",
|
||||
"89afd3a9249f5a8d3edda07d84ca049d": "找不到 {{Polymorphic}} 模型:“{0}”",
|
||||
"89bf6d92731fe7bd2146ce8d0bec205c": "无效的自变量,必须是字符串、{{regex}} 字面值或 {{RegExp}} 对象",
|
||||
"8a39126103a157f501affa070367a1b0": "{0} 实例无效。详细信息:{1}。",
|
||||
"8c5ab01638c1ac1d58168c6346a8481a": "无效的 {{regex}} 标志:{0}",
|
||||
"938401ea4ce48159efa9be1d4a5e8bab": "项必须是数组:{0}",
|
||||
"9e1f143ee02946324d34da92f71bf74e": "{0} 关系:{1} 需要参数 `model`",
|
||||
"a004f310d315e592843776fab964eaeb": "{{Polymorphic}} 关系需要直通模型",
|
||||
"a0cf0e09c26df14283223e84e6a10f00": "无法更新属性。包含 {{id}} {0} 的 {{Object}} 不存在!",
|
||||
"a2487abefef4259c2131d96cdb8543b1": "连接失败:{0}\n将在下一次请求时重试。",
|
||||
"a25e41a39c60c4702e55d0c3936576a1": "键不匹配:{0}.{1}: {2}, {3}.{4}: {5}",
|
||||
"a327355560d495454fba2c1aad6bdf09": "未知的作用域方法:{0}",
|
||||
"a6c18a7f4390cd3d59a2a7a047ae2aab": "运行“{{npm install loopback-datasource-juggler}} {0}”命令",
|
||||
"a829dee089c912e68c18920ba015400c": "警告:对于以下模型,{{id}} 属性无法从 {0} 更改为 {1}:{{'loaded'}} 操作挂钩中的 {2}",
|
||||
"a984a076c59e451948b2bcf7a393d860": "警告:对于以下模型,{{id}} 属性无法从 {0} 更改为 {1}:{{'before save'}} 操作挂钩中的 {2}",
|
||||
"ac04cf275b71c1eb89a41cf6bbad7a64": "不推荐使用 HasOne 方法“getAsync()”,请改为使用“get()”。",
|
||||
"b138294f132edfe1eb2a8211150c7238": "查询中存在意外的“未定义”",
|
||||
"b15b20280211ad258d92947f05b6e4a5": "连接器尚未进行初始化。",
|
||||
"b278876ec93ef9760f00e83f38ba313d": "不推荐使用 Scope 方法“getAsync()”,请改为使用“find()”。",
|
||||
"ba0fd8106eb54de4d003a844206431fd": "不推荐使用模型挂钩“{0}”,请改用操作挂钩。{{http://docs.strongloop.com/display/LB/Operation+hooks}}",
|
||||
"baf2c8b0c5a574b8a894e9b6304fece1": "Where 子句 {0} 不是 {{object}}",
|
||||
"bdb11cc1c780c9ccac33c316cfdc9d82": "未针对属性 {0}.{1} 定义类型",
|
||||
"bdfb951c8ff7ce0cbc08c06f548fd927": "值为空的 {{object}}",
|
||||
"bec226891a505828bfc76c5cfd73b336": "无法获取未知键 {0} 的 TTL",
|
||||
"cd930369e86cdd222f7bd117c6f9fa94": "未知的缺省值提供程序 {0}",
|
||||
"cfee4d8149316d9a647c0885cf3cafaf": "不支持包含点的属性名称。模型:{0},动态属性:{1}",
|
||||
"d40328eabd8756d795bcdd49d782d4e9": "数据源不支持事务",
|
||||
"da02dd6c53d4148320eeb31718a7aebe": "属性 {0} 的类型无效",
|
||||
"da751a8a748adbde5b55fa83b707b4e2": "不支持包含点的属性名称。模型:{0},属性:{1}",
|
||||
"db03083e9a768388fdbee865249ac67a": "正在忽略 {{updateOrCreate()}} 中的验证错误:",
|
||||
"dd63416d9b7d9fa4181e89efd619dfd8": "值不是具有有序数字索引的 {{array}} 或 {{object}}",
|
||||
"ddf0aa14803f1c84f4a97f3803f7471c": "类名是必需的",
|
||||
"e08ab0e1ab55f26c357061447b635905": "在 {0} 中找不到以下项的关系:({1}.{2} ,{3}.{4})",
|
||||
"e0e9504e137a3c3339144b51ed76fef2": "连接器定义不正确:应创建数据源的“{{connector}}”成员",
|
||||
"e2f282cbe3efba001d6d3a09f7f6ca8c": "{{polymorphic}} {0} 关系:提供参数 `polymorphic.discriminator` 时,{1} 需要参数 `polymorphic.foreignKey`",
|
||||
"e39e0f5d52bfbf511e645d19ecadd2fa": "{0} 属性有无效子句 {1}:{2}",
|
||||
"e4434de4bb8f5a3cd1d416e4d80d7e0b": "未知的“{0}”{{id}}“{1}”。",
|
||||
"e54d944c2a2c85a23caa86027ae307cf": "无法迁移未附加到此数据源的模型:{0}",
|
||||
"e54f118a75e15e132f16b985274eb46d": "查询过滤器 {0} 不是 {{object}}",
|
||||
"e55937649d8d7a11706b8cec22d02eae": "{{HasOne}} 关系 {0} 为空",
|
||||
"e6161ae8459c79d810e2aa9d21282a39": "在更新属性时必须提供 {{id}}!",
|
||||
"eb56c2b0c30cf006e2df00a549ec9c2c": "未针对 {1} 模型定义关系“{0}”",
|
||||
"ec42dca074f1818c447f7ad16e2d01af": "连接的连接器未提供 {0}",
|
||||
"ecb7aa804bf54c682999d20d6436104c": "{{transaction}} 不活动:{0}",
|
||||
"f30809cb932b72a66416a709c8531530": "连接器不支持某个事务内的 {{method}}",
|
||||
"f41bd91dc0f000a79c0bf842f1b7fdf9": "无法从 JSON 字符串创建列表:{0}",
|
||||
"f6e8c96c93b9c7687d6c172b3695e898": "{{id}} 属性 ({0}) 无法从 {1} 更新为 {2}",
|
||||
"fa9ae17e8e008d0eb0f0421a2972308c": "{{polymorphic}} {0} 关系:{1} 需要参数 `model`",
|
||||
"fca4d12faff1035d9d0438d73432571b": "{0}.{1} 的重复条目",
|
||||
"fd3cc89dc67e2d604eaae21bdf41d403": "针对模型 {1} 找不到关系 {0}",
|
||||
"fec8ebda24db46a9d040bf863765cc44": "{0} 操作员有无效子句 {1}:{2}"
|
||||
}
|
||||
|
|
@ -0,0 +1,97 @@
|
|||
{
|
||||
"0483a77cf77741504204e5c066597487": "{{polymorphic}} {0} 關係:定義自訂 'foreignKey'/'discriminator' 時 {1} 並未預期參數 'polymorphic.as'",
|
||||
"09483e03b91c8bd58732a74b3ef1ec13": "無效日期:{0}",
|
||||
"0a5aa17f7866a85e3aee37ef5369403c": "LinkManyToMany 收到的目標不含必要的 \"{0}\"",
|
||||
"0b16d3ffc42f91b4b9a4b3b50c41c838": "順序 {0} 無效",
|
||||
"0bd753a8944ad0af85a939bb25273887": "無法使不明金鑰 {0} 到期",
|
||||
"0c0b867aca0973ba26e887d3337cc4ec": "找不到 {{Polymorphic}} 模型:`{0}` 未設定",
|
||||
"0c4eb8b6c2ff6e51d7e195eee346ced9": "表格 '{0}' 不存在。",
|
||||
"0ff31abb394afb555df162e74ff1a0a0": "當 {{forceId}} 設定為 true 時,無法將 {{id}} 從 {0} 更新為 {1}",
|
||||
"1ae7d3e0be381efb32bfd1ba652f5172": "警告:{{polymorphic}} {0} 關係:{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": "{{polymorphic}} {0} 關係:{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": "對於 {1},找不到 id 為 {0} 的實例",
|
||||
"416dfbb7b823f51c9f3800be81060b41": "對於 {1},找不到 {{id}} 為 {0} 的實例",
|
||||
"49b5afd8c6a19ad9c8abeffb2f8114eb": "BelongsTo 方法 \"getAsync()\" 已淘汰,請改用 \"get()\"。",
|
||||
"4c78325cedbb826db3a05bf5df0e8546": "取代時必須提供 {{id}}!",
|
||||
"4e31b1edd10dadb724d83387de0b5062": "{{limit}} 參數 {0} 無效",
|
||||
"514985b2327f061ffb1c932f6b909979": "模型 {0} 未定義。",
|
||||
"525c856e65daab43be247e7b5410febd": "{{polymorphic}} {0} 關係:定義自訂 'foreignKey'/'discriminator' 時 {1} 並未預期參數 'polymorphic.selector'",
|
||||
"5c18ee111dd87540cdb19a2a93b33be9": "由於逾時,已回復交易",
|
||||
"5ec7e6664256f7ea78f4f06dafc7d974": "交易尚未備妥,等候解析傳回的承諾",
|
||||
"5ec8efeb715a2c34b440f2d76e2cf87d": " {0}",
|
||||
"6111399276924ffa3bc9a410cdfcb2e5": "沒有 {{id}} 名稱 {0}",
|
||||
"614e3355647e4127c96256102dc63376": "{0} 內容具有無效的子句 {1}:預期為字串或 RegExp",
|
||||
"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": "{{polymorphic}} {0} 關係:提供參數 'polymorphic.foreignKey' 時 {1} 需要參數 'polymorphic.discriminator'",
|
||||
"791ab3031a73ede03f7d6299a85e8289": "連接 {0} 毫秒之後逾時",
|
||||
"7b277018e43d41bc445731092b91547d": "未連接",
|
||||
"7bbbdece4eea90e42aa5c0bce295e503": "{{Model::findById}} 需要 {{id}} 引數",
|
||||
"7e9530c0399289be0ee601a604be71ff": "{{BelongsTo}} 關係 {0} 是空的",
|
||||
"7faa840eb6ce11250a141deb42a6c489": "不明關係 {{scope}}:{0}",
|
||||
"8091838319a5cc7d6a34af2f2a616ce9": "在模型中,內容名稱不應該為 \"{{constructor}}\":{0}",
|
||||
"881e4b0cb86ed59549248ee540a9fd10": "{0} 資料中不接受內容名稱 \"{{constructor}}\"",
|
||||
"89afd3a9249f5a8d3edda07d84ca049d": "找不到 {{Polymorphic}} 模型:`{0}`",
|
||||
"89bf6d92731fe7bd2146ce8d0bec205c": "引數無效,必須是字串、{{regex}} 文字或 {{RegExp}} 物件",
|
||||
"8a39126103a157f501affa070367a1b0": "{0} 實例無效。詳細資料:{1}。",
|
||||
"8c5ab01638c1ac1d58168c6346a8481a": "無效 {{regex}} 旗標:{0}",
|
||||
"938401ea4ce48159efa9be1d4a5e8bab": "項目必須是陣列:{0}",
|
||||
"9e1f143ee02946324d34da92f71bf74e": "{0} 關係:{1} 需要參數 'model'",
|
||||
"a004f310d315e592843776fab964eaeb": "{{Polymorphic}} 關係需要有通過模型",
|
||||
"a0cf0e09c26df14283223e84e6a10f00": "無法更新屬性。{{id}} 為 {0} 的 {{Object}} 不存在!",
|
||||
"a2487abefef4259c2131d96cdb8543b1": "連線失敗:{0}\n將在下一次要求時重試。",
|
||||
"a25e41a39c60c4702e55d0c3936576a1": "金鑰不符:{0}.{1}:{2},{3}.{4}:{5}",
|
||||
"a327355560d495454fba2c1aad6bdf09": "不明範圍方法:{0}",
|
||||
"a6c18a7f4390cd3d59a2a7a047ae2aab": "執行 \"{{npm install loopback-datasource-juggler}} {0}\" 指令",
|
||||
"a829dee089c912e68c18920ba015400c": "警告:在 {{'loaded'}} 作業連結鉤中,無法將 model:{2} 的 {{id}} 內容從 {0} 變更為 {1}",
|
||||
"a984a076c59e451948b2bcf7a393d860": "警告:在 {{'before save'}} 作業連結鉤中,無法將 model:{2} 的 {{id}} 內容從 {0} 變更為 {1}",
|
||||
"ac04cf275b71c1eb89a41cf6bbad7a64": "HasOne 方法 \"getAsync()\" 已淘汰,請改用 \"get()\"。",
|
||||
"b138294f132edfe1eb2a8211150c7238": "查詢中有非預期的 `undefined`",
|
||||
"b15b20280211ad258d92947f05b6e4a5": "尚未起始設定連接器。",
|
||||
"b278876ec93ef9760f00e83f38ba313d": "Scope 方法 \"getAsync()\" 已淘汰,請改用 \"find()\"。",
|
||||
"ba0fd8106eb54de4d003a844206431fd": "模型連結鉤 \"{0}\" 已淘汰,請改用作業連結鉤。{{http://docs.strongloop.com/display/LB/Operation+hooks}}",
|
||||
"baf2c8b0c5a574b8a894e9b6304fece1": "where 子句 {0} 不是 {{object}}",
|
||||
"bdb11cc1c780c9ccac33c316cfdc9d82": "未針對內容 {0}.{1} 定義類型",
|
||||
"bdfb951c8ff7ce0cbc08c06f548fd927": "值是空的 {{object}}",
|
||||
"bec226891a505828bfc76c5cfd73b336": "無法取得不明金鑰 {0} 的 TTL",
|
||||
"cd930369e86cdd222f7bd117c6f9fa94": "預設值提供者 {0} 不明",
|
||||
"cfee4d8149316d9a647c0885cf3cafaf": "不支援含有點的內容名稱。模型:{0},動態內容:{1}",
|
||||
"d40328eabd8756d795bcdd49d782d4e9": "資料來源不支援交易",
|
||||
"da02dd6c53d4148320eeb31718a7aebe": "內容 {0} 的類型無效",
|
||||
"da751a8a748adbde5b55fa83b707b4e2": "不支援含有點的內容名稱。模型:{0},內容:{1}",
|
||||
"db03083e9a768388fdbee865249ac67a": "忽略 {{updateOrCreate()}} 中的驗證錯誤:",
|
||||
"dd63416d9b7d9fa4181e89efd619dfd8": "值不是具有循序數值索引的 {{array}} 或 {{object}}",
|
||||
"ddf0aa14803f1c84f4a97f3803f7471c": "需要類別名稱",
|
||||
"e08ab0e1ab55f26c357061447b635905": "在 {0} 中找不到 ({1}.{2},{3}.{4}) 的關係",
|
||||
"e0e9504e137a3c3339144b51ed76fef2": "未正確定義連接器:應該建立資料來源的 `{{connector}}` 成員",
|
||||
"e2f282cbe3efba001d6d3a09f7f6ca8c": "{{polymorphic}} {0} 關係:提供參數 'polymorphic.discriminator' 時 {1} 需要參數 'polymorphic.foreignKey'",
|
||||
"e39e0f5d52bfbf511e645d19ecadd2fa": "{0} 內容具有無效的子句 {1}:{2}",
|
||||
"e4434de4bb8f5a3cd1d416e4d80d7e0b": "\"{0}\" {{id}} \"{1}\" 不明。",
|
||||
"e54d944c2a2c85a23caa86027ae307cf": "無法移轉未連接至這個資料來源的模型:{0}",
|
||||
"e54f118a75e15e132f16b985274eb46d": "查詢過濾器 {0} 不是 {{object}}",
|
||||
"e55937649d8d7a11706b8cec22d02eae": "{{HasOne}} 關係 {0} 是空的",
|
||||
"e6161ae8459c79d810e2aa9d21282a39": "更新屬性時必須提供 {{id}}!",
|
||||
"eb56c2b0c30cf006e2df00a549ec9c2c": "未定義 {1} 模型的關係 \"{0}\"",
|
||||
"ec42dca074f1818c447f7ad16e2d01af": "連接的連接器未提供 {0}",
|
||||
"ecb7aa804bf54c682999d20d6436104c": "{{transaction}} 為非作用中:{0}",
|
||||
"f30809cb932b72a66416a709c8531530": "連接器在交易內不支援 {{method}}",
|
||||
"f41bd91dc0f000a79c0bf842f1b7fdf9": "無法從 JSON 字串建立 List:{0}",
|
||||
"f6e8c96c93b9c7687d6c172b3695e898": "無法將 {{id}} 內容 ({0}) 從 {1} 更新為 {2}",
|
||||
"fa9ae17e8e008d0eb0f0421a2972308c": "{{polymorphic}} {0} 關係:{1} 需要參數 'model'",
|
||||
"fca4d12faff1035d9d0438d73432571b": "{0}.{1} 的重複項目",
|
||||
"fd3cc89dc67e2d604eaae21bdf41d403": "找不到模型 {1} 的關係 {0}",
|
||||
"fec8ebda24db46a9d040bf863765cc44": "{0} 運算子具有無效的子句 {1}:{2}"
|
||||
}
|
||||
|
|
@ -1,6 +1,13 @@
|
|||
// 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';
|
||||
|
||||
// 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;
|
||||
|
|
|
@ -0,0 +1,242 @@
|
|||
// 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';
|
||||
|
||||
const g = require('strong-globalize')();
|
||||
|
||||
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) {
|
||||
const settings = dataSource.settings;
|
||||
dataSource.connector = new KeyValueMemoryConnector(settings, dataSource);
|
||||
if (cb) process.nextTick(cb);
|
||||
};
|
||||
|
||||
function KeyValueMemoryConnector(settings, dataSource) {
|
||||
Connector.call(this, 'kv-memory', settings);
|
||||
|
||||
debug('Connector settings', settings);
|
||||
|
||||
this.dataSource = dataSource;
|
||||
this.DataAccessObject = dataSource.juggler.KeyValueAccessObject;
|
||||
|
||||
this._store = Object.create(null);
|
||||
|
||||
this._setupRegularCleanup();
|
||||
}
|
||||
util.inherits(KeyValueMemoryConnector, Connector);
|
||||
|
||||
KeyValueMemoryConnector.prototype._setupRegularCleanup = function() {
|
||||
// Scan the database for expired keys at a regular interval
|
||||
// in order to release memory. Note that GET operation checks
|
||||
// key expiration too, the scheduled cleanup is merely a performance
|
||||
// optimization.
|
||||
this._cleanupTimer = setInterval(
|
||||
() => {
|
||||
if (this && this._removeExpiredItems) {
|
||||
this._removeExpiredItems();
|
||||
} else {
|
||||
// The datasource/connector was destroyed - cancel the timer
|
||||
clearInterval(this._cleanupTimer);
|
||||
}
|
||||
},
|
||||
1000,
|
||||
);
|
||||
this._cleanupTimer.unref();
|
||||
};
|
||||
|
||||
KeyValueMemoryConnector._removeExpiredItems = function() {
|
||||
debug('Running scheduled cleanup of expired items.');
|
||||
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];
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
KeyValueMemoryConnector.prototype._getStoreForModel = function(modelName) {
|
||||
if (!(modelName in this._store)) {
|
||||
this._store[modelName] = Object.create(null);
|
||||
}
|
||||
return this._store[modelName];
|
||||
};
|
||||
|
||||
KeyValueMemoryConnector.prototype._removeIfExpired = function(modelName, key) {
|
||||
const store = this._getStoreForModel(modelName);
|
||||
let item = store[key];
|
||||
if (item && item.isExpired()) {
|
||||
debug('Removing expired key', key);
|
||||
delete store[key];
|
||||
item = undefined;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
KeyValueMemoryConnector.prototype.get =
|
||||
function(modelName, key, options, callback) {
|
||||
this._removeIfExpired(modelName, key);
|
||||
|
||||
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)) {
|
||||
value = new Buffer(value.slice(7), 'base64');
|
||||
} else if (/^date:/.test(value)) {
|
||||
value = new Date(value.slice(5));
|
||||
} else if (value != null) {
|
||||
value = JSON.parse(value);
|
||||
}
|
||||
|
||||
process.nextTick(function() {
|
||||
callback(null, value);
|
||||
});
|
||||
};
|
||||
|
||||
KeyValueMemoryConnector.prototype.set =
|
||||
function(modelName, key, value, options, callback) {
|
||||
const store = this._getStoreForModel(modelName);
|
||||
if (Buffer.isBuffer(value)) {
|
||||
value = 'buffer:' + value.toString('base64');
|
||||
} else if (value instanceof Date) {
|
||||
value = 'date:' + value.toISOString();
|
||||
} else {
|
||||
value = JSON.stringify(value);
|
||||
}
|
||||
|
||||
debug('SET %j %j %s %j', modelName, key, value, options);
|
||||
store[key] = new StoreItem(value, options && options.ttl);
|
||||
|
||||
process.nextTick(callback);
|
||||
};
|
||||
|
||||
KeyValueMemoryConnector.prototype.expire =
|
||||
function(modelName, key, ttl, options, callback) {
|
||||
this._removeIfExpired(modelName, key);
|
||||
|
||||
const store = this._getStoreForModel(modelName);
|
||||
|
||||
if (!(key in store)) {
|
||||
return process.nextTick(function() {
|
||||
const err = new Error(g.f('Cannot expire unknown key %j', key));
|
||||
err.statusCode = 404;
|
||||
callback(err);
|
||||
});
|
||||
}
|
||||
|
||||
debug('EXPIRE %j %j %s', modelName, key, ttl || '(never)');
|
||||
store[key].setTtl(ttl);
|
||||
process.nextTick(callback);
|
||||
};
|
||||
|
||||
KeyValueMemoryConnector.prototype.ttl =
|
||||
function(modelName, key, options, callback) {
|
||||
this._removeIfExpired(modelName, key);
|
||||
|
||||
const store = this._getStoreForModel(modelName);
|
||||
|
||||
// key is unknown
|
||||
if (!(key in store)) {
|
||||
return process.nextTick(function() {
|
||||
const err = new Error(g.f('Cannot get TTL for unknown key %j', key));
|
||||
err.statusCode = 404;
|
||||
callback(err);
|
||||
});
|
||||
}
|
||||
|
||||
const ttl = store[key].getTtl();
|
||||
debug('TTL %j %j -> %s', modelName, key, ttl);
|
||||
|
||||
process.nextTick(function() {
|
||||
callback(null, ttl);
|
||||
});
|
||||
};
|
||||
|
||||
KeyValueMemoryConnector.prototype.iterateKeys =
|
||||
function(modelName, filter, options, callback) {
|
||||
const store = this._getStoreForModel(modelName);
|
||||
const self = this;
|
||||
const checkFilter = createMatcher(filter.match);
|
||||
|
||||
const keys = Object.keys(store).filter(function(key) {
|
||||
return !self._removeIfExpired(modelName, key) && checkFilter(key);
|
||||
});
|
||||
|
||||
debug('ITERATE KEYS %j -> %s keys', modelName, keys.length);
|
||||
|
||||
let ix = 0;
|
||||
return {
|
||||
next: function(cb) {
|
||||
const value = ix < keys.length ? keys[ix++] : undefined;
|
||||
setImmediate(function() { cb(null, value); });
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
function createMatcher(pattern) {
|
||||
if (!pattern) return function matchAll() { return true; };
|
||||
|
||||
return minimatch.filter(pattern, {
|
||||
nobrace: true,
|
||||
noglobstar: true,
|
||||
dot: true,
|
||||
noext: true,
|
||||
nocomment: true,
|
||||
});
|
||||
}
|
||||
|
||||
KeyValueMemoryConnector.prototype.disconnect = function(callback) {
|
||||
if (this._cleanupTimer)
|
||||
clearInterval(this._cleanupTimer);
|
||||
this._cleanupTimer = null;
|
||||
process.nextTick(callback);
|
||||
};
|
||||
|
||||
KeyValueMemoryConnector.prototype.delete =
|
||||
function(modelName, key, options, callback) {
|
||||
const store = this._getStoreForModel(modelName);
|
||||
delete store[key];
|
||||
callback();
|
||||
};
|
||||
|
||||
KeyValueMemoryConnector.prototype.deleteAll =
|
||||
function(modelName, options, callback) {
|
||||
const modelStore = this._getStoreForModel(modelName);
|
||||
for (const key in modelStore)
|
||||
delete modelStore[key];
|
||||
callback();
|
||||
};
|
||||
|
||||
function StoreItem(value, ttl) {
|
||||
this.value = value;
|
||||
this.setTtl(ttl);
|
||||
}
|
||||
|
||||
StoreItem.prototype.isExpired = function() {
|
||||
return this.expires && this.expires <= Date.now();
|
||||
};
|
||||
|
||||
StoreItem.prototype.setTtl = function(ttl) {
|
||||
if (ttl) {
|
||||
this.expires = Date.now() + ttl;
|
||||
} else {
|
||||
this.expires = undefined;
|
||||
}
|
||||
};
|
||||
|
||||
StoreItem.prototype.getTtl = function() {
|
||||
return !this.expires ? undefined : this.expires - Date.now();
|
||||
};
|
File diff suppressed because it is too large
Load Diff
|
@ -1,7 +1,15 @@
|
|||
var util = require('util');
|
||||
var Connector = require('loopback-connector').Connector;
|
||||
var utils = require('../utils');
|
||||
var crypto = require('crypto');
|
||||
// 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';
|
||||
|
||||
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
|
||||
|
@ -42,7 +50,7 @@ Transient.prototype.getTypes = function() {
|
|||
return ['db', 'nosql', 'transient'];
|
||||
};
|
||||
|
||||
Transient.prototype.connect = function (callback) {
|
||||
Transient.prototype.connect = function(callback) {
|
||||
if (this.isTransaction) {
|
||||
this.onTransactionExec = callback;
|
||||
} else {
|
||||
|
@ -51,40 +59,41 @@ 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
|
||||
return crypto.randomBytes(Math.ceil(24 / 2))
|
||||
.toString('hex') // convert to hexadecimal format
|
||||
.slice(0, 24); // return required number of characters
|
||||
}
|
||||
};
|
||||
|
||||
Transient.prototype.exists = function exists(model, id, callback) {
|
||||
process.nextTick(function () { callback(null, false); }.bind(this));
|
||||
process.nextTick(function() { callback(null, false); }.bind(this));
|
||||
};
|
||||
|
||||
Transient.prototype.find = function find(model, id, callback) {
|
||||
process.nextTick(function () { callback(null, null); }.bind(this));
|
||||
process.nextTick(function() { callback(null, null); }.bind(this));
|
||||
};
|
||||
|
||||
Transient.prototype.all = function all(model, filter, callback) {
|
||||
process.nextTick(function () { callback(null, []); });
|
||||
process.nextTick(function() { callback(null, []); });
|
||||
};
|
||||
|
||||
Transient.prototype.count = function count(model, callback, where) {
|
||||
process.nextTick(function () { callback(null, 0); });
|
||||
process.nextTick(function() { callback(null, 0); });
|
||||
};
|
||||
|
||||
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);
|
||||
}
|
||||
|
@ -97,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('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 {
|
||||
|
@ -131,15 +140,15 @@ Transient.prototype.destroyAll = function destroyAll(model, where, callback) {
|
|||
* Flush the cache - noop.
|
||||
* @param {Function} callback
|
||||
*/
|
||||
Transient.prototype.flush = function (action, result, callback) {
|
||||
process.nextTick(function () { callback && callback(null, result); });
|
||||
Transient.prototype.flush = function(action, result, callback) {
|
||||
process.nextTick(function() { callback && callback(null, result); });
|
||||
};
|
||||
|
||||
Transient.prototype.transaction = function () {
|
||||
Transient.prototype.transaction = function() {
|
||||
return new Transient(this);
|
||||
};
|
||||
|
||||
Transient.prototype.exec = function (callback) {
|
||||
Transient.prototype.exec = function(callback) {
|
||||
this.onTransactionExec();
|
||||
setTimeout(callback, 50);
|
||||
};
|
||||
|
|
2571
lib/dao.js
2571
lib/dao.js
File diff suppressed because it is too large
Load Diff
1617
lib/datasource.js
1617
lib/datasource.js
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,110 @@
|
|||
// 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';
|
||||
|
||||
const inspect = require('util').inspect;
|
||||
|
||||
module.exports = DateString;
|
||||
|
||||
/**
|
||||
* A String whose value is a valid representation of a Date.
|
||||
* Use this type if you need to preserve the format of the value and still
|
||||
* check if it's valid.
|
||||
* Example:
|
||||
* ```js
|
||||
* var loopback = require('loopback');
|
||||
* var dt = new loopback.DateString('2001-01-01');
|
||||
*
|
||||
* dt.toString();
|
||||
* // '2001-01-01'
|
||||
* dt._date.toISOString();
|
||||
* // '2001-01-01T00:00:00.000Z'
|
||||
* ```
|
||||
*
|
||||
* You can use this definition on your models as well:
|
||||
* ```json
|
||||
* {
|
||||
* "name": "Person",
|
||||
* "base": "PersistedModel",
|
||||
* "properties": {
|
||||
* "name": {
|
||||
* "type": "string"
|
||||
* },
|
||||
* "dob": {
|
||||
* "type": "DateString",
|
||||
* "required": true
|
||||
* },
|
||||
* },
|
||||
* "validations": [],
|
||||
* "relations": {},
|
||||
* "acls": [],
|
||||
* "methods": {}
|
||||
* }
|
||||
* ```
|
||||
* @class DateString
|
||||
* @param {String} value
|
||||
* @constructor
|
||||
*/
|
||||
function DateString(value) {
|
||||
if (!(this instanceof DateString)) {
|
||||
return new DateString(value);
|
||||
}
|
||||
|
||||
if (value instanceof DateString) {
|
||||
value = value.when;
|
||||
}
|
||||
|
||||
if (typeof(value) !== 'string') {
|
||||
throw new Error('Input must be a string');
|
||||
}
|
||||
|
||||
Object.defineProperty(this, 'when', {
|
||||
get: () => { return this._when; },
|
||||
set: (val) => {
|
||||
const d = new Date(val);
|
||||
if (isNaN(d.getTime())) {
|
||||
throw new Error('Invalid date');
|
||||
} else {
|
||||
this._when = val;
|
||||
this._date = d;
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
this.when = value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the value of DateString in its original form.
|
||||
* @returns {String} The Date as a String.
|
||||
*/
|
||||
DateString.prototype.toString = function() {
|
||||
return this.when;
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns the JSON representation of the DateString object.
|
||||
* @returns {String} A JSON string.
|
||||
*/
|
||||
DateString.prototype.toJSON = function() {
|
||||
return JSON.stringify({
|
||||
when: this.when,
|
||||
});
|
||||
};
|
||||
|
||||
DateString.prototype.inspect = function(depth, options) {
|
||||
return 'DateString ' + inspect({
|
||||
when: this.when,
|
||||
_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;
|
||||
}
|
229
lib/geo.js
229
lib/geo.js
|
@ -1,106 +1,146 @@
|
|||
var assert = require('assert');
|
||||
// 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';
|
||||
|
||||
const assert = require('assert');
|
||||
|
||||
/*!
|
||||
* Get a near filter from a given where object. For connector use only.
|
||||
*/
|
||||
|
||||
exports.nearFilter = function nearFilter(where) {
|
||||
var result = false;
|
||||
const nearResults = [];
|
||||
nearSearch(where);
|
||||
return (!nearResults.length ? false : nearResults);
|
||||
|
||||
if (where && typeof where === 'object') {
|
||||
Object.keys(where).forEach(function (key) {
|
||||
var ex = where[key];
|
||||
function nearSearch(clause, parentKeys) {
|
||||
if (typeof clause !== 'object') {
|
||||
return false;
|
||||
}
|
||||
parentKeys = parentKeys || [];
|
||||
|
||||
if (ex && ex.near) {
|
||||
result = {
|
||||
near: ex.near,
|
||||
maxDistance: ex.maxDistance,
|
||||
unit: ex.unit,
|
||||
key: key
|
||||
};
|
||||
Object.keys(clause).forEach(function(clauseKey) {
|
||||
if (typeof clause[clauseKey] !== 'object' || !clause[clauseKey]) return;
|
||||
if (Array.isArray(clause[clauseKey])) {
|
||||
clause[clauseKey].forEach(function(el, index) {
|
||||
const ret = nearSearch(el, parentKeys.concat(clauseKey).concat(index));
|
||||
if (ret) return ret;
|
||||
});
|
||||
} else {
|
||||
if (clause[clauseKey].hasOwnProperty('near')) {
|
||||
const result = clause[clauseKey];
|
||||
nearResults.push({
|
||||
near: result.near,
|
||||
maxDistance: result.maxDistance,
|
||||
minDistance: result.minDistance,
|
||||
unit: result.unit,
|
||||
// If key is at root, define a single string, otherwise append it to the full path array
|
||||
mongoKey: parentKeys.length ? parentKeys.concat(clauseKey) : clauseKey,
|
||||
key: clauseKey,
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
/*!
|
||||
* Filter a set of objects using the given `nearFilter`.
|
||||
* Filter a set of results using the given filters returned by `nearFilter()`.
|
||||
* Can support multiple locations, but will include results from all of them.
|
||||
*
|
||||
* WARNING: "or" operator with GeoPoint does not work as expected, eg:
|
||||
* {where: {or: [{location: {near: (29,-90)}},{name:'Sean'}]}}
|
||||
* Will actually work as if you had used "and". This is because geo filtering
|
||||
* takes place outside of the SQL query, so the result set of "name = Sean" is
|
||||
* returned by the database, and then the location filtering happens in the app
|
||||
* logic. So the "near" operator is always an "and" of the SQL filters, and "or"
|
||||
* of other GeoPoint filters.
|
||||
*
|
||||
* Additionally, since this step occurs after the SQL result set is returned,
|
||||
* if using GeoPoints with pagination the result set may be smaller than the
|
||||
* page size. The page size is enforced at the DB level, and then we may
|
||||
* remove results at the Geo-app level. If we "limit: 25", but 4 of those results
|
||||
* do not have a matching geopoint field, the request will only return 21 results.
|
||||
* This may make it erroneously look like a given page is the end of the result set.
|
||||
*/
|
||||
|
||||
exports.filter = function (arr, filter) {
|
||||
var origin = filter.near;
|
||||
var max = filter.maxDistance > 0 ? filter.maxDistance : false;
|
||||
var unit = filter.unit;
|
||||
var key = filter.key;
|
||||
exports.filter = function(rawResults, filters) {
|
||||
const distances = {};
|
||||
const results = [];
|
||||
|
||||
// create distance index
|
||||
var distances = {};
|
||||
var result = [];
|
||||
filters.forEach(function(filter) {
|
||||
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;
|
||||
|
||||
arr.forEach(function (obj) {
|
||||
var loc = obj[key];
|
||||
// create distance index
|
||||
rawResults.forEach(function(result) {
|
||||
let loc = result[key];
|
||||
|
||||
// filter out objects without locations
|
||||
if (!loc) return;
|
||||
// filter out results without locations
|
||||
if (!loc) return;
|
||||
|
||||
if (!(loc instanceof GeoPoint)) {
|
||||
loc = GeoPoint(loc);
|
||||
}
|
||||
if (!(loc instanceof GeoPoint)) loc = GeoPoint(loc);
|
||||
|
||||
if (typeof loc.lat !== 'number') return;
|
||||
if (typeof loc.lng !== 'number') return;
|
||||
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});
|
||||
|
||||
if (max && d > max) {
|
||||
// dont add
|
||||
} else {
|
||||
distances[obj.id] = d;
|
||||
result.push(obj);
|
||||
}
|
||||
// filter result if distance is either < minDistance or > maxDistance
|
||||
if ((min && d < min) || (max && d > max)) return;
|
||||
|
||||
distances[result.id] = d;
|
||||
results.push(result);
|
||||
});
|
||||
|
||||
results.sort(function(resA, resB) {
|
||||
const a = resA[key];
|
||||
const b = resB[key];
|
||||
|
||||
if (a && b) {
|
||||
const da = distances[resA.id];
|
||||
const db = distances[resB.id];
|
||||
|
||||
if (db === da) return 0;
|
||||
return da > db ? 1 : -1;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
return result.sort(function (objA, objB) {
|
||||
var a = objA[key];
|
||||
var b = objB[key];
|
||||
|
||||
if (a && b) {
|
||||
var da = distances[objA.id];
|
||||
var db = distances[objB.id];
|
||||
|
||||
if (db === da) return 0;
|
||||
return da > db ? 1 : -1;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
});
|
||||
}
|
||||
return results;
|
||||
};
|
||||
|
||||
exports.GeoPoint = GeoPoint;
|
||||
|
||||
/**
|
||||
/**
|
||||
* The GeoPoint object represents a physical location.
|
||||
*
|
||||
*
|
||||
* For example:
|
||||
*
|
||||
*
|
||||
* ```js
|
||||
* var loopback = require(‘loopback’);
|
||||
* var here = new loopback.GeoPoint({lat: 10.32424, lng: 5.84978});
|
||||
* ```
|
||||
*
|
||||
*
|
||||
* Embed a latitude / longitude point in a model.
|
||||
*
|
||||
*
|
||||
* ```js
|
||||
* var CoffeeShop = loopback.createModel('coffee-shop', {
|
||||
* location: 'GeoPoint'
|
||||
* });
|
||||
* ```
|
||||
*
|
||||
*
|
||||
* You can query LoopBack models with a GeoPoint property and an attached data source using geo-spatial filters and
|
||||
* sorting. For example, the following code finds the three nearest coffee shops.
|
||||
*
|
||||
*
|
||||
* ```js
|
||||
* CoffeeShop.attachTo(oracle);
|
||||
* var here = new GeoPoint({lat: 10.32424, lng: 5.84978});
|
||||
|
@ -109,9 +149,9 @@ exports.GeoPoint = GeoPoint;
|
|||
* });
|
||||
* ```
|
||||
* @class GeoPoint
|
||||
* @property {Number} lat The latitude in degrees.
|
||||
* @property {Number} lng The longitude in degrees.
|
||||
*
|
||||
* @property {Number} lat The latitude in degrees.
|
||||
* @property {Number} lng The longitude in degrees.
|
||||
*
|
||||
* @options {Object} Options Object with two Number properties: lat and long.
|
||||
* @property {Number} lat The latitude point in degrees. Range: -90 to 90.
|
||||
* @property {Number} lng The longitude point in degrees. Range: -180 to 180.
|
||||
|
@ -126,23 +166,28 @@ function GeoPoint(data) {
|
|||
return new GeoPoint(data);
|
||||
}
|
||||
|
||||
if(arguments.length === 2) {
|
||||
if (arguments.length === 2) {
|
||||
data = {
|
||||
lat: arguments[0],
|
||||
lng: arguments[1]
|
||||
lng: arguments[1],
|
||||
};
|
||||
}
|
||||
|
||||
assert(Array.isArray(data) || typeof data === 'object' || typeof data === 'string', 'must provide valid geo-coordinates array [lat, lng] or object or a "lat, lng" string');
|
||||
assert(Array.isArray(data) || typeof data === 'object' || typeof data === 'string',
|
||||
'must provide valid geo-coordinates array [lat, lng] or object or a "lat, lng" string');
|
||||
|
||||
if (typeof data === 'string') {
|
||||
data = data.split(/,\s*/);
|
||||
assert(data.length === 2, 'must provide a string "lat,lng" creating a GeoPoint with a string');
|
||||
try {
|
||||
data = JSON.parse(data);
|
||||
} catch (err) {
|
||||
data = data.split(/,\s*/);
|
||||
assert(data.length === 2, 'must provide a string "lat,lng" creating a GeoPoint with a string');
|
||||
}
|
||||
}
|
||||
if (Array.isArray(data)) {
|
||||
data = {
|
||||
lat: Number(data[0]),
|
||||
lng: Number(data[1])
|
||||
lng: Number(data[1]),
|
||||
};
|
||||
} else {
|
||||
data.lng = Number(data.lng);
|
||||
|
@ -157,18 +202,18 @@ function GeoPoint(data) {
|
|||
assert(data.lat <= 90, 'lat must be <= 90');
|
||||
assert(data.lat >= -90, 'lat must be >= -90');
|
||||
|
||||
this.lat = data.lat;
|
||||
this.lat = data.lat;
|
||||
this.lng = data.lng;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine the spherical distance between two GeoPoints.
|
||||
*
|
||||
*
|
||||
* @param {GeoPoint} pointA Point A
|
||||
* @param {GeoPoint} pointB Point B
|
||||
* @options {Object} options Options object with one key, 'type'. See below.
|
||||
* @property {String} type Unit of measurement, one of:
|
||||
*
|
||||
*
|
||||
* - `miles` (default)
|
||||
* - `radians`
|
||||
* - `kilometers`
|
||||
|
@ -186,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);
|
||||
};
|
||||
|
@ -200,16 +245,16 @@ GeoPoint.distanceBetween = function distanceBetween(a, b, options) {
|
|||
* Example:
|
||||
* ```js
|
||||
* var loopback = require(‘loopback’);
|
||||
*
|
||||
*
|
||||
* var here = new loopback.GeoPoint({lat: 10, lng: 10});
|
||||
* var there = new loopback.GeoPoint({lat: 5, lng: 5});
|
||||
*
|
||||
*
|
||||
* loopback.GeoPoint.distanceBetween(here, there, {type: 'miles'}) // 438
|
||||
* ```
|
||||
* @param {Object} point GeoPoint object to which to measure distance.
|
||||
* @options {Object} options Options object with one key, 'type'. See below.
|
||||
* @property {String} type Unit of measurement, one of:
|
||||
*
|
||||
*
|
||||
* - `miles` (default)
|
||||
* - `radians`
|
||||
* - `kilometers`
|
||||
|
@ -219,7 +264,7 @@ GeoPoint.distanceBetween = function distanceBetween(a, b, options) {
|
|||
* - `degrees`
|
||||
*/
|
||||
|
||||
GeoPoint.prototype.distanceTo = function (point, options) {
|
||||
GeoPoint.prototype.distanceTo = function(point, options) {
|
||||
return GeoPoint.distanceBetween(this, point, options);
|
||||
};
|
||||
|
||||
|
@ -227,7 +272,7 @@ GeoPoint.prototype.distanceTo = function (point, options) {
|
|||
* Simple serialization.
|
||||
*/
|
||||
|
||||
GeoPoint.prototype.toString = function () {
|
||||
GeoPoint.prototype.toString = function() {
|
||||
return this.lat + ',' + this.lng;
|
||||
};
|
||||
|
||||
|
@ -238,24 +283,23 @@ 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,
|
||||
feet: 20902200,
|
||||
radians: 1,
|
||||
degrees: RAD2DEG
|
||||
degrees: RAD2DEG,
|
||||
};
|
||||
|
||||
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;
|
||||
|
@ -263,14 +307,13 @@ function geoDistance(x1, y1, x2, y2, options) {
|
|||
x2 = x2 * DEG2RAD;
|
||||
y2 = y2 * DEG2RAD;
|
||||
|
||||
// use the haversine formula to calculate distance for any 2 points on a sphere.
|
||||
// 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];
|
||||
}
|
||||
|
||||
|
|
46
lib/hooks.js
46
lib/hooks.js
|
@ -1,4 +1,12 @@
|
|||
var deprecated = require('depd')('loopback-datasource-juggler');
|
||||
// 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';
|
||||
|
||||
const deprecated = require('depd')('loopback-datasource-juggler');
|
||||
const g = require('strong-globalize')();
|
||||
|
||||
/*!
|
||||
* Module exports
|
||||
|
@ -28,18 +36,28 @@ Hookable.afterUpdate = null;
|
|||
Hookable.beforeDestroy = null;
|
||||
Hookable.afterDestroy = null;
|
||||
|
||||
// TODO: Evaluate https://github.com/bnoguchi/hooks-js/
|
||||
/**
|
||||
* @deprecated
|
||||
* Setup a Model-based hook to trigger when the specified action occurs.
|
||||
* The trigger is broken up into three segments: `beforeHook`, `work` and
|
||||
* `afterHook`.
|
||||
* @param {string} actionName The name of the action that triggers the hook.
|
||||
* @param {Function} work The 2nd phase of the trigger.
|
||||
* @param {*} data The value(s) to provide to the 1st phase (`beforeHook`) call.
|
||||
* @callback
|
||||
* @param {Function} callback
|
||||
*/
|
||||
Hookable.prototype.trigger = function trigger(actionName, work, data, callback) {
|
||||
var capitalizedName = capitalize(actionName);
|
||||
var beforeHook = this.constructor["before" + capitalizedName]
|
||||
|| this.constructor["pre" + capitalizedName];
|
||||
var afterHook = this.constructor["after" + capitalizedName]
|
||||
|| this.constructor["post" + capitalizedName];
|
||||
const capitalizedName = capitalize(actionName);
|
||||
let beforeHook = this.constructor['before' + capitalizedName] ||
|
||||
this.constructor['pre' + 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)
|
||||
|
@ -52,7 +70,7 @@ Hookable.prototype.trigger = function trigger(actionName, work, data, callback)
|
|||
if (work) {
|
||||
if (beforeHook) {
|
||||
// before hook should be called on instance with two parameters: next and data
|
||||
beforeHook.call(inst, function () {
|
||||
beforeHook.call(inst, function() {
|
||||
// Check arguments to next(err, result)
|
||||
if (arguments.length) {
|
||||
return callback && callback.apply(null, arguments);
|
||||
|
@ -82,14 +100,14 @@ 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;
|
||||
deprecated('Model hook "' + hookName + '" is deprecated, ' +
|
||||
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');
|
||||
'{{http://docs.strongloop.com/display/LB/Operation+hooks}}', hookName));
|
||||
}
|
||||
|
|
610
lib/include.js
610
lib/include.js
File diff suppressed because it is too large
Load Diff
|
@ -1,8 +1,31 @@
|
|||
// 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.
|
||||
|
@ -12,28 +35,27 @@ 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);
|
||||
}
|
||||
return kvMap;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Yeah, it joins. You need three things id -> obj1 map, id -> [obj2] map and merge function.
|
||||
* This functions will take each obj1, locate all data to join in map2 and call merge function.
|
||||
|
@ -42,57 +64,56 @@ 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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Map with arbitrary keys and values. User .set() and .get() to work with values instead of []
|
||||
* @returns {{set: Function, get: Function, remove: Function, exist: Function, getKeys: Function}}
|
||||
* @constructor
|
||||
*/
|
||||
function KVMap(){
|
||||
var _originalKeyFieldName = 'originalKey';
|
||||
var _valueKeyFieldName = 'value';
|
||||
var _dict = {};
|
||||
var keyToString = function(key){ return key.toString() };
|
||||
var mapImpl = {
|
||||
set: function(key, value){
|
||||
var recordObj = {};
|
||||
function KVMap() {
|
||||
const _originalKeyFieldName = 'originalKey';
|
||||
const _valueKeyFieldName = 'value';
|
||||
const _dict = {};
|
||||
const keyToString = function(key) { return key.toString(); };
|
||||
const mapImpl = {
|
||||
set: function(key, value) {
|
||||
const recordObj = {};
|
||||
recordObj[_originalKeyFieldName] = key;
|
||||
recordObj[_valueKeyFieldName] = value;
|
||||
_dict[keyToString(key)] = recordObj;
|
||||
return true;
|
||||
},
|
||||
get: function(key){
|
||||
var storeObj = _dict[keyToString(key)];
|
||||
if(storeObj) {
|
||||
get: function(key) {
|
||||
const storeObj = _dict[keyToString(key)];
|
||||
if (storeObj) {
|
||||
return storeObj[_valueKeyFieldName];
|
||||
} else {
|
||||
return undefined;
|
||||
}
|
||||
},
|
||||
remove: function(key){
|
||||
remove: function(key) {
|
||||
delete _dict[keyToString(key)];
|
||||
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) {
|
||||
getKeys: function() {
|
||||
const result = [];
|
||||
for (const key in _dict) {
|
||||
result.push(_dict[key][_originalKeyFieldName]);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
},
|
||||
|
||||
};
|
||||
return mapImpl;
|
||||
|
|
|
@ -1,21 +1,26 @@
|
|||
// 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.exports = function getIntrospector(ModelBuilder) {
|
||||
|
||||
function introspectType(value) {
|
||||
|
||||
// Unknown type, using Any
|
||||
if (value === null || value === undefined) {
|
||||
return ModelBuilder.Any;
|
||||
}
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
@ -24,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;
|
||||
}
|
||||
|
@ -42,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;
|
||||
|
@ -57,6 +62,4 @@ module.exports = function getIntrospector(ModelBuilder) {
|
|||
|
||||
ModelBuilder.introspect = introspectType;
|
||||
return introspectType;
|
||||
}
|
||||
|
||||
|
||||
};
|
||||
|
|
45
lib/jutil.js
45
lib/jutil.js
|
@ -1,23 +1,30 @@
|
|||
var util = require('util');
|
||||
// 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';
|
||||
|
||||
const util = require('util');
|
||||
|
||||
/**
|
||||
*
|
||||
* @param newClass
|
||||
* @param baseClass
|
||||
*/
|
||||
exports.inherits = function (newClass, baseClass, options) {
|
||||
exports.inherits = function(newClass, baseClass, options) {
|
||||
util.inherits(newClass, baseClass);
|
||||
|
||||
options = options || {
|
||||
staticProperties: true,
|
||||
override: false
|
||||
override: false,
|
||||
};
|
||||
|
||||
if (options.staticProperties) {
|
||||
Object.keys(baseClass).forEach(function (classProp) {
|
||||
if (classProp !== 'super_' && (!newClass.hasOwnProperty(classProp)
|
||||
|| options.override)) {
|
||||
var pd = Object.getOwnPropertyDescriptor(baseClass, classProp);
|
||||
Object.keys(baseClass).forEach(function(classProp) {
|
||||
if (classProp !== 'super_' && (!newClass.hasOwnProperty(classProp) ||
|
||||
options.override)) {
|
||||
const pd = Object.getOwnPropertyDescriptor(baseClass, classProp);
|
||||
Object.defineProperty(newClass, classProp, pd);
|
||||
}
|
||||
});
|
||||
|
@ -30,7 +37,7 @@ exports.inherits = function (newClass, baseClass, options) {
|
|||
* @param mixinClass The class to be mixed in
|
||||
* @param options
|
||||
*/
|
||||
exports.mixin = function (newClass, mixinClass, options) {
|
||||
exports.mixin = function(newClass, mixinClass, options) {
|
||||
if (Array.isArray(newClass._mixins)) {
|
||||
if (newClass._mixins.indexOf(mixinClass) !== -1) {
|
||||
return;
|
||||
|
@ -44,7 +51,7 @@ exports.mixin = function (newClass, mixinClass, options) {
|
|||
staticProperties: true,
|
||||
instanceProperties: true,
|
||||
override: false,
|
||||
proxyFunctions: false
|
||||
proxyFunctions: false,
|
||||
};
|
||||
|
||||
if (options.staticProperties === undefined) {
|
||||
|
@ -67,14 +74,14 @@ 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;
|
||||
Object.keys(sourceScope).forEach(function(propertyName) {
|
||||
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);
|
||||
|
@ -89,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);
|
||||
}
|
||||
|
|
|
@ -0,0 +1,68 @@
|
|||
// 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';
|
||||
|
||||
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.
|
||||
*
|
||||
* @options {Object} options Unused ATM, placeholder for future options.
|
||||
* @callback {Function} callback
|
||||
* @param {Error} err Error object.
|
||||
* @promise
|
||||
*
|
||||
* @header KVAO.prototype.deleteAll([options, ]cb)
|
||||
*/
|
||||
module.exports = function deleteAll(options, callback) {
|
||||
if (callback == undefined && typeof options === 'function') {
|
||||
callback = options;
|
||||
options = {};
|
||||
} else if (!options) {
|
||||
options = {};
|
||||
}
|
||||
|
||||
assert(typeof options === 'object', 'options must be an object');
|
||||
|
||||
callback = callback || utils.createPromiseCallback();
|
||||
|
||||
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 {
|
||||
const errMsg = 'Connector does not support key-value pair deletion';
|
||||
debug(errMsg);
|
||||
process.nextTick(function() {
|
||||
const err = new Error(errMsg);
|
||||
err.statusCode = 501;
|
||||
callback(err);
|
||||
});
|
||||
}
|
||||
return callback.promise;
|
||||
};
|
||||
|
||||
function iterateAndDelete(connector, modelName, options, callback) {
|
||||
const iter = connector.iterateKeys(modelName, {});
|
||||
const keys = [];
|
||||
iter.next(onNextKey);
|
||||
|
||||
function onNextKey(err, key) {
|
||||
if (err) return callback(err);
|
||||
if (key === undefined) return callback();
|
||||
connector.delete(modelName, key, options, onDeleted);
|
||||
}
|
||||
|
||||
function onDeleted(err) {
|
||||
if (err) return callback(err);
|
||||
iter.next(onNextKey);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,50 @@
|
|||
// 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';
|
||||
|
||||
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.
|
||||
*
|
||||
* @param {String} key Key to use when searching the database.
|
||||
* @options {Object} options
|
||||
* @callback {Function} callback
|
||||
* @param {Error} err Error object.
|
||||
* @param {*} result Value associated with the given key.
|
||||
* @promise
|
||||
*
|
||||
* @header KVAO.prototype.delete(key[, options], cb)
|
||||
*/
|
||||
module.exports = function keyValueDelete(key, options, callback) {
|
||||
if (callback == undefined && typeof options === 'function') {
|
||||
callback = options;
|
||||
options = {};
|
||||
} else if (!options) {
|
||||
options = {};
|
||||
}
|
||||
|
||||
assert(typeof key === 'string' && key, 'key must be a non-empty string');
|
||||
|
||||
callback = callback || utils.createPromiseCallback();
|
||||
|
||||
const connector = this.getConnector();
|
||||
if (typeof connector.delete === 'function') {
|
||||
connector.delete(this.modelName, key, options, callback);
|
||||
} else {
|
||||
const errMsg = 'Connector does not support key-value pair deletion';
|
||||
debug(errMsg);
|
||||
process.nextTick(function() {
|
||||
const err = new Error(errMsg);
|
||||
err.statusCode = 501;
|
||||
callback(err);
|
||||
});
|
||||
}
|
||||
|
||||
return callback.promise;
|
||||
};
|
|
@ -0,0 +1,39 @@
|
|||
// 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';
|
||||
|
||||
const assert = require('assert');
|
||||
const utils = require('../utils');
|
||||
|
||||
/**
|
||||
* Set the TTL (time to live) in ms (milliseconds) for a given key. TTL is the
|
||||
* remaining time before a key-value pair is discarded from the database.
|
||||
*
|
||||
* @param {String} key Key to use when searching the database.
|
||||
* @param {Number} ttl TTL in ms to set for the key.
|
||||
* @options {Object} options
|
||||
* @callback {Function} callback
|
||||
* @param {Error} err Error object.
|
||||
* @promise
|
||||
*
|
||||
* @header KVAO.expire(key, ttl, cb)
|
||||
*/
|
||||
module.exports = function keyValueExpire(key, ttl, options, callback) {
|
||||
if (callback == undefined && typeof options === 'function') {
|
||||
callback = options;
|
||||
options = {};
|
||||
} else if (!options) {
|
||||
options = {};
|
||||
}
|
||||
|
||||
assert(typeof key === 'string' && key, 'key must be a non-empty string');
|
||||
assert(typeof ttl === 'number' && ttl > 0, 'ttl must be a positive integer');
|
||||
assert(typeof options === 'object', 'options must be an object');
|
||||
|
||||
callback = callback || utils.createPromiseCallback();
|
||||
this.getConnector().expire(this.modelName, key, ttl, options, callback);
|
||||
return callback.promise;
|
||||
};
|
|
@ -0,0 +1,39 @@
|
|||
// 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';
|
||||
|
||||
const assert = require('assert');
|
||||
const utils = require('../utils');
|
||||
|
||||
/**
|
||||
* Return the value associated with a given key.
|
||||
*
|
||||
* @param {String} key Key to use when searching the database.
|
||||
* @options {Object} options
|
||||
* @callback {Function} callback
|
||||
* @param {Error} err Error object.
|
||||
* @param {*} result Value associated with the given key.
|
||||
* @promise
|
||||
*
|
||||
* @header KVAO.get(key, cb)
|
||||
*/
|
||||
module.exports = function keyValueGet(key, options, callback) {
|
||||
if (callback == undefined && typeof options === 'function') {
|
||||
callback = options;
|
||||
options = {};
|
||||
} else if (!options) {
|
||||
options = {};
|
||||
}
|
||||
|
||||
assert(typeof key === 'string' && key, 'key must be a non-empty string');
|
||||
|
||||
callback = callback || utils.createPromiseCallback();
|
||||
this.getConnector().get(this.modelName, key, options, function(err, result) {
|
||||
// TODO convert raw result to Model instance (?)
|
||||
callback(err, result);
|
||||
});
|
||||
return callback.promise;
|
||||
};
|
|
@ -0,0 +1,25 @@
|
|||
// 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;
|
||||
|
||||
KeyValueAccessObject.delete = require('./delete');
|
||||
KeyValueAccessObject.deleteAll = require('./delete-all');
|
||||
KeyValueAccessObject.get = require('./get');
|
||||
KeyValueAccessObject.set = require('./set');
|
||||
KeyValueAccessObject.expire = require('./expire');
|
||||
KeyValueAccessObject.ttl = require('./ttl');
|
||||
KeyValueAccessObject.iterateKeys = require('./iterate-keys');
|
||||
KeyValueAccessObject.keys = require('./keys');
|
||||
|
||||
KeyValueAccessObject.getConnector = function() {
|
||||
return this.getDataSource().connector;
|
||||
};
|
||||
|
|
@ -0,0 +1,43 @@
|
|||
// 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';
|
||||
|
||||
const assert = require('assert');
|
||||
const utils = require('../utils');
|
||||
|
||||
/**
|
||||
* Asynchronously iterate all keys in the database. Similar to `.keys()` but
|
||||
* instead allows for iteration over large data sets without having to load
|
||||
* everything into memory at once.
|
||||
*
|
||||
* @param {Object} filter An optional filter object with the following
|
||||
* @param {String} filter.match Glob string to use to filter returned
|
||||
* keys (i.e. `userid.*`). All connectors are required to support `*` and
|
||||
* `?`. They may also support additional special characters that are
|
||||
* specific to the backing database.
|
||||
* @param {Object} options
|
||||
* @returns {AsyncIterator} An Object implementing `next(cb) -> Promise`
|
||||
* function that can be used to iterate all keys.
|
||||
*
|
||||
* @header KVAO.iterateKeys(filter)
|
||||
*/
|
||||
module.exports = function keyValueIterateKeys(filter, options) {
|
||||
filter = filter || {};
|
||||
options = options || {};
|
||||
|
||||
assert(typeof filter === 'object', 'filter must be an object');
|
||||
assert(typeof options === 'object', 'options must be an object');
|
||||
|
||||
const iter = this.getConnector().iterateKeys(this.modelName, filter, options);
|
||||
// promisify the returned iterator
|
||||
return {
|
||||
next: function(callback) {
|
||||
callback = callback || utils.createPromiseCallback();
|
||||
iter.next(callback);
|
||||
return callback.promise;
|
||||
},
|
||||
};
|
||||
};
|
|
@ -0,0 +1,61 @@
|
|||
// 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';
|
||||
|
||||
const assert = require('assert');
|
||||
const utils = require('../utils');
|
||||
|
||||
/**
|
||||
* Return all keys in the database.
|
||||
*
|
||||
* **WARNING**: This method is not suitable for large data sets as all
|
||||
* key-values pairs are loaded into memory at once. For large data sets,
|
||||
* use `iterateKeys()` instead.
|
||||
*
|
||||
* @param {Object} filter An optional filter object with the following
|
||||
* @param {String} filter.match Glob string used to filter returned
|
||||
* keys (i.e. `userid.*`). All connectors are required to support `*` and
|
||||
* `?`, but may also support additional special characters specific to the
|
||||
* database.
|
||||
* @param {Object} options
|
||||
* @callback {Function} callback
|
||||
* @promise
|
||||
*
|
||||
*
|
||||
* @header KVAO.keys(filter, callback)
|
||||
*/
|
||||
module.exports = function keyValueKeys(filter, options, callback) {
|
||||
if (callback === undefined) {
|
||||
if (typeof options === 'function') {
|
||||
callback = options;
|
||||
options = undefined;
|
||||
} else if (options === undefined && typeof filter === 'function') {
|
||||
callback = filter;
|
||||
filter = undefined;
|
||||
}
|
||||
}
|
||||
|
||||
filter = filter || {};
|
||||
options = options || {};
|
||||
|
||||
assert(typeof filter === 'object', 'filter must be an object');
|
||||
assert(typeof options === 'object', 'options must be an object');
|
||||
|
||||
callback = callback || utils.createPromiseCallback();
|
||||
|
||||
const iter = this.iterateKeys(filter, options);
|
||||
const keys = [];
|
||||
iter.next(onNextKey);
|
||||
|
||||
function onNextKey(err, key) {
|
||||
if (err) return callback(err);
|
||||
if (key === undefined) return callback(null, keys);
|
||||
keys.push(key);
|
||||
iter.next(onNextKey);
|
||||
}
|
||||
|
||||
return callback.promise;
|
||||
};
|
|
@ -0,0 +1,49 @@
|
|||
// 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';
|
||||
|
||||
const assert = require('assert');
|
||||
const utils = require('../utils');
|
||||
|
||||
/**
|
||||
* Persist a value and associate it with the given key.
|
||||
*
|
||||
* @param {String} key Key to associate with the given value.
|
||||
* @param {*} value Value to persist.
|
||||
* @options {Number|Object} options Optional settings for the key-value
|
||||
* pair. If a Number is provided, it is set as the TTL (time to live) in ms
|
||||
* (milliseconds) for the key-value pair.
|
||||
* @property {Number} ttl TTL for the key-value pair in ms.
|
||||
* @callback {Function} callback
|
||||
* @param {Error} err Error object.
|
||||
* @promise
|
||||
*
|
||||
* @header KVAO.set(key, value, cb)
|
||||
*/
|
||||
module.exports = function keyValueSet(key, value, options, callback) {
|
||||
if (callback == undefined && typeof options === 'function') {
|
||||
callback = options;
|
||||
options = {};
|
||||
} else if (typeof options === 'number') {
|
||||
options = {ttl: options};
|
||||
} else if (!options) {
|
||||
options = {};
|
||||
}
|
||||
|
||||
assert(typeof key === 'string' && key, 'key must be a non-empty string');
|
||||
assert(value != null, 'value must be defined and not null');
|
||||
assert(typeof options === 'object', 'options must be an object');
|
||||
if (options && 'ttl' in options) {
|
||||
assert(typeof options.ttl === 'number' && options.ttl > 0,
|
||||
'options.ttl must be a positive number');
|
||||
}
|
||||
|
||||
callback = callback || utils.createPromiseCallback();
|
||||
|
||||
// TODO convert possible model instance in "value" to raw data via toObect()
|
||||
this.getConnector().set(this.modelName, key, value, options, callback);
|
||||
return callback.promise;
|
||||
};
|
|
@ -0,0 +1,39 @@
|
|||
// 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';
|
||||
|
||||
const assert = require('assert');
|
||||
const utils = require('../utils');
|
||||
|
||||
/**
|
||||
* Return the TTL (time to live) for a given key. TTL is the remaining time
|
||||
* before a key-value pair is discarded from the database.
|
||||
*
|
||||
* @param {String} key Key to use when searching the database.
|
||||
* @options {Object} options
|
||||
* @callback {Function} callback
|
||||
* @param {Error} error
|
||||
* @param {Number} ttl Expiration time for the key-value pair. `undefined` if
|
||||
* TTL was not initially set.
|
||||
* @promise
|
||||
*
|
||||
* @header KVAO.ttl(key, cb)
|
||||
*/
|
||||
module.exports = function keyValueTtl(key, options, callback) {
|
||||
if (callback == undefined && typeof options === 'function') {
|
||||
callback = options;
|
||||
options = {};
|
||||
} else if (!options) {
|
||||
options = {};
|
||||
}
|
||||
|
||||
assert(typeof key === 'string' && key, 'key must be a non-empty string');
|
||||
assert(typeof options === 'object', 'options must be an object');
|
||||
|
||||
callback = callback || utils.createPromiseCallback();
|
||||
this.getConnector().ttl(this.modelName, key, options, callback);
|
||||
return callback.promise;
|
||||
};
|
95
lib/list.js
95
lib/list.js
|
@ -1,10 +1,21 @@
|
|||
var util = require('util');
|
||||
var Any = require('./types').Types.Any;
|
||||
// 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';
|
||||
|
||||
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);
|
||||
}
|
||||
|
@ -13,23 +24,28 @@ function List(items, itemType, parent) {
|
|||
try {
|
||||
items = JSON.parse(items);
|
||||
} catch (e) {
|
||||
var err = new Error(util.format('could not create List from JSON string: %j', items));
|
||||
const err = new Error(g.f('could not create List from JSON string: %j', items));
|
||||
err.statusCode = 400;
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
|
||||
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 || [];
|
||||
if (!Array.isArray(items)) {
|
||||
var err = new Error(util.format('Items must be an array: %j', items));
|
||||
const err = new Error(g.f('Items must be an array: %j', items));
|
||||
err.statusCode = 400;
|
||||
throw err;
|
||||
}
|
||||
|
||||
if(!itemType) {
|
||||
if (!itemType) {
|
||||
itemType = items[0] && items[0].constructor;
|
||||
}
|
||||
|
||||
|
@ -37,30 +53,32 @@ function List(items, itemType, parent) {
|
|||
itemType = itemType[0];
|
||||
}
|
||||
|
||||
if(itemType === Array) {
|
||||
if (itemType === Array) {
|
||||
itemType = Any;
|
||||
}
|
||||
|
||||
Object.defineProperty(arr, 'itemType', {
|
||||
writable: true,
|
||||
enumerable: false,
|
||||
value: itemType
|
||||
value: itemType,
|
||||
});
|
||||
|
||||
if (parent) {
|
||||
// List constructor now called with actual model instance
|
||||
Object.defineProperty(arr, 'parent', {
|
||||
writable: true,
|
||||
enumerable: false,
|
||||
value: parent
|
||||
value: parent,
|
||||
});
|
||||
}
|
||||
|
||||
items.forEach(function (item, i) {
|
||||
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;
|
||||
|
@ -68,18 +86,37 @@ function List(items, itemType, parent) {
|
|||
|
||||
util.inherits(List, Array);
|
||||
|
||||
var _push = List.prototype.push;
|
||||
const _push = List.prototype.push;
|
||||
|
||||
List.prototype.push = function (obj) {
|
||||
var item = this.itemType && (obj instanceof this.itemType) ? obj : this.itemType(obj);
|
||||
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) {
|
||||
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 = [];
|
||||
this.forEach(function (item) {
|
||||
if (item && typeof item === 'object' && item.toObject) {
|
||||
List.prototype.toObject = function(onlySchema, removeHidden, removeProtected) {
|
||||
const items = [];
|
||||
this.forEach(function(item) {
|
||||
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);
|
||||
|
@ -88,11 +125,27 @@ List.prototype.toObject = function (onlySchema, removeHidden, removeProtected) {
|
|||
return items;
|
||||
};
|
||||
|
||||
List.prototype.toJSON = function () {
|
||||
/**
|
||||
* Convert itself to a plain array.
|
||||
*
|
||||
* Some modules such as `should` checks prototype for comparison
|
||||
*/
|
||||
List.prototype.toArray = function() {
|
||||
const items = [];
|
||||
this.forEach(function(item) {
|
||||
items.push(item);
|
||||
});
|
||||
return items;
|
||||
};
|
||||
|
||||
List.prototype.toJSON = function() {
|
||||
return this.toObject(true);
|
||||
};
|
||||
|
||||
List.prototype.toString = function () {
|
||||
List.prototype.toString = function() {
|
||||
return JSON.stringify(this.toJSON());
|
||||
};
|
||||
|
||||
function isClass(fn) {
|
||||
return fn && fn.toString().indexOf('class ') === 0;
|
||||
}
|
||||
|
|
|
@ -1,6 +1,13 @@
|
|||
var debug = require('debug')('loopback:mixin');
|
||||
var assert = require('assert');
|
||||
var DefaultModelBaseClass = require('./model.js');
|
||||
// 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';
|
||||
|
||||
const debug = require('debug')('loopback:mixin');
|
||||
const assert = require('assert');
|
||||
const DefaultModelBaseClass = require('./model.js');
|
||||
|
||||
function isModelClass(cls) {
|
||||
if (!cls) {
|
||||
|
@ -23,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 || {});
|
||||
|
@ -34,12 +41,14 @@ MixinProvider.prototype.applyMixin = function applyMixin(modelClass, name, optio
|
|||
}
|
||||
} else {
|
||||
// Try model name
|
||||
var model = this.modelBuilder.getModel(name);
|
||||
if(model) {
|
||||
const model = this.modelBuilder.getModel(name);
|
||||
if (model) {
|
||||
debug('Mixin is resolved to a model: %s', name);
|
||||
modelClass.mixin(model, options);
|
||||
} else {
|
||||
debug('Invalid mixin: %s', name);
|
||||
const errMsg = 'Model "' + modelClass.modelName + '" uses unknown mixin: ' + name;
|
||||
debug(errMsg);
|
||||
throw new Error(errMsg);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -57,11 +66,10 @@ MixinProvider.prototype.define = function defineMixin(name, mixin) {
|
|||
debug('Defining mixin: %s', name);
|
||||
}
|
||||
if (isModelClass(mixin)) {
|
||||
this.mixins[name] = function (Model, options) {
|
||||
this.mixins[name] = function(Model, options) {
|
||||
Model.mixin(mixin, options);
|
||||
};
|
||||
} else if (typeof mixin === 'function') {
|
||||
this.mixins[name] = mixin;
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -1,22 +1,36 @@
|
|||
// 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 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 mergeSettings = require('./utils').mergeSettings;
|
||||
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
|
||||
|
@ -26,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.
|
||||
|
@ -39,6 +53,7 @@ function ModelBuilder() {
|
|||
// create blank models pool
|
||||
this.models = {};
|
||||
this.definitions = {};
|
||||
this.settings = {};
|
||||
this.mixins = new MixinProvider(this);
|
||||
this.defaultModelBaseClass = DefaultModelBaseClass;
|
||||
}
|
||||
|
@ -61,10 +76,10 @@ function isModelClass(cls) {
|
|||
*
|
||||
* @param {String} name The model name
|
||||
* @param {Boolean} forceCreate Whether the create a stub for the given name if a model doesn't exist.
|
||||
* @returns {*} The model class
|
||||
* @returns {ModelClass} The model class
|
||||
*/
|
||||
ModelBuilder.prototype.getModel = function (name, forceCreate) {
|
||||
var model = this.models[name];
|
||||
ModelBuilder.prototype.getModel = function(name, forceCreate) {
|
||||
let model = this.models[name];
|
||||
if (!model && forceCreate) {
|
||||
model = this.define(name, {}, {unresolved: true});
|
||||
}
|
||||
|
@ -76,7 +91,7 @@ ModelBuilder.prototype.getModel = function (name, forceCreate) {
|
|||
* @param {String} name The model name
|
||||
* @returns {ModelDefinition} The model definition
|
||||
*/
|
||||
ModelBuilder.prototype.getModelDefinition = function (name) {
|
||||
ModelBuilder.prototype.getModelDefinition = function(name) {
|
||||
return this.definitions[name];
|
||||
};
|
||||
|
||||
|
@ -106,20 +121,20 @@ ModelBuilder.prototype.getModelDefinition = function (name) {
|
|||
* @param {Object} properties Hash of class properties in format `{property: Type, property2: Type2, ...}` or `{property: {type: Type}, property2: {type: Type2}, ...}`
|
||||
* @param {Object} settings Other configuration of class
|
||||
* @param {Function} parent Parent model
|
||||
* @return newly created class
|
||||
* @return {ModelClass} The class constructor.
|
||||
*
|
||||
*/
|
||||
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('Class name required');
|
||||
throw new Error(g.f('Class name required'));
|
||||
}
|
||||
if (args.length === 1) {
|
||||
properties = {};
|
||||
|
@ -139,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;
|
||||
|
@ -154,6 +169,15 @@ ModelBuilder.prototype.define = function defineClass(className, properties, sett
|
|||
}
|
||||
}
|
||||
|
||||
// Assert current model's base class provides method `getMergePolicy()`.
|
||||
assert(ModelBaseClass.getMergePolicy, `Base class ${ModelBaseClass.modelName}
|
||||
does not provide method getMergePolicy(). Most likely it is not inheriting
|
||||
from datasource-juggler's built-in default ModelBaseClass, which is an
|
||||
incorrect usage of the framework.`);
|
||||
|
||||
// Initialize base model inheritance rank if not set already
|
||||
ModelBaseClass.__rank = ModelBaseClass.__rank || 1;
|
||||
|
||||
// Make sure base properties are inherited
|
||||
// See https://github.com/strongloop/loopback-datasource-juggler/issues/293
|
||||
if ((parent && !settings.base) || (!parent && settings.base)) {
|
||||
|
@ -161,47 +185,29 @@ 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.
|
||||
if (!ModelClass || !ModelClass.settings.unresolved) {
|
||||
// every class can receive hash of data as optional param
|
||||
ModelClass = function ModelConstructor(data, options) {
|
||||
if (!(this instanceof ModelConstructor)) {
|
||||
return new ModelConstructor(data, options);
|
||||
}
|
||||
if (ModelClass.settings.unresolved) {
|
||||
throw new Error('Model ' + ModelClass.modelName + ' is not defined.');
|
||||
}
|
||||
ModelBaseClass.apply(this, arguments);
|
||||
};
|
||||
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') {
|
||||
if (f !== 'on') {
|
||||
ModelClass[f] = EventEmitter.prototype[f].bind(events);
|
||||
continue;
|
||||
}
|
||||
|
||||
// report deprecation warnings at the time Model.on() is called
|
||||
ModelClass.on = function(event) {
|
||||
if (['changed', 'deleted', 'deletedAll'].indexOf(event) !== -1) {
|
||||
deprecated(this.modelName + '\'s event "' + event + '" ' +
|
||||
'is deprecated, use Operation hooks instead. ' +
|
||||
'http://docs.strongloop.com/display/LB/Operation+hooks');
|
||||
}
|
||||
EventEmitter.prototype.on.apply(events, arguments);
|
||||
};
|
||||
ModelClass[f] = EventEmitter.prototype[f].bind(events);
|
||||
}
|
||||
}
|
||||
hiddenProperty(ModelClass, 'modelName', className);
|
||||
}
|
||||
|
||||
// Iterate sub model inheritance rank over base model rank
|
||||
ModelClass.__rank = ModelBaseClass.__rank + 1;
|
||||
|
||||
util.inherits(ModelClass, ModelBaseClass);
|
||||
|
||||
// store class in model pool
|
||||
|
@ -222,12 +228,13 @@ ModelBuilder.prototype.define = function defineClass(className, properties, sett
|
|||
// Support both flavors path: 'x' and path: '/x'
|
||||
pathName = '/' + pathName;
|
||||
}
|
||||
hiddenProperty(ModelClass, 'http', { path: pathName });
|
||||
hiddenProperty(ModelClass, 'http', {path: pathName});
|
||||
hiddenProperty(ModelClass, 'base', ModelBaseClass);
|
||||
hiddenProperty(ModelClass, '_observers', {});
|
||||
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];
|
||||
|
@ -236,8 +243,8 @@ 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];
|
||||
Object.keys(settings.models).forEach(function(m) {
|
||||
const model = settings.models[m];
|
||||
ModelClass[m] = typeof model === 'string' ? modelBuilder.getModel(model, true) : model;
|
||||
});
|
||||
}
|
||||
|
@ -245,21 +252,36 @@ ModelBuilder.prototype.define = function defineClass(className, properties, sett
|
|||
ModelClass.getter = {};
|
||||
ModelClass.setter = {};
|
||||
|
||||
for (var p in properties) {
|
||||
// Remove properties that reverted by the subclass
|
||||
if (properties[p] === null || properties[p] === false) {
|
||||
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
|
||||
// automatically and 'id' property shouldn't be there for SOAP models. idInjection = false will not work
|
||||
// for SOAP generator case, since base 'Model' has already id property. 'id: false' at the property level will not
|
||||
// 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.
|
||||
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)) {
|
||||
// Hide the base property
|
||||
delete properties[p];
|
||||
}
|
||||
|
||||
// Warn about properties with unsupported names
|
||||
// Throw error for properties with unsupported names
|
||||
if (/\./.test(p)) {
|
||||
deprecated('Property names containing a dot are not supported. ' +
|
||||
'Model: ' + className + ', property: ' + p);
|
||||
throw new Error(g.f('Property names containing dot(s) are not supported. ' +
|
||||
'Model: %s, property: %s', className, p));
|
||||
}
|
||||
|
||||
// Warn if property name is 'constructor'
|
||||
if (p === 'constructor') {
|
||||
deprecated(g.f('Property name should not be "{{constructor}}" in Model: %s', className));
|
||||
}
|
||||
}
|
||||
|
||||
var modelDefinition = new ModelDefinition(this, className, properties, settings);
|
||||
const modelDefinition = new ModelDefinition(this, className, properties, settings);
|
||||
|
||||
this.definitions[className] = modelDefinition;
|
||||
|
||||
|
@ -268,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;
|
||||
|
@ -283,51 +305,77 @@ ModelBuilder.prototype.define = function defineClass(className, properties, sett
|
|||
// Add the id property
|
||||
if (idInjection) {
|
||||
// Set up the id property
|
||||
ModelClass.definition.defineProperty('id', { type: Number, id: 1, generated: true });
|
||||
ModelClass.definition.defineProperty('id', {type: Number, id: 1, generated: true});
|
||||
}
|
||||
|
||||
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];
|
||||
get: function() {
|
||||
const idProp = ModelClass.definition.idNames()[0];
|
||||
return this.__data[idProp];
|
||||
},
|
||||
configurable: true,
|
||||
enumerable: false
|
||||
enumerable: false,
|
||||
});
|
||||
}
|
||||
} else {
|
||||
// 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++) {
|
||||
get: function() {
|
||||
const compositeId = {};
|
||||
const idNames = ModelClass.definition.idNames();
|
||||
for (let i = 0, p; i < idNames.length; i++) {
|
||||
p = idNames[i];
|
||||
compositeId[p] = this.__data[p];
|
||||
}
|
||||
return compositeId;
|
||||
},
|
||||
configurable: true,
|
||||
enumerable: false
|
||||
enumerable: false,
|
||||
});
|
||||
}
|
||||
|
||||
// updateOnly property is added to indicate that this property will appear in
|
||||
// the model for update/updateorcreate operations but and not for create operation.
|
||||
let forceId = ModelClass.settings.forceId;
|
||||
if (idNames.length > 0) {
|
||||
const idName = modelDefinition.idName();
|
||||
const idProp = ModelClass.definition.rawProperties[idName];
|
||||
if (idProp.generated && forceId !== false) {
|
||||
forceId = 'auto';
|
||||
} else if (!idProp.generated && forceId === 'auto') {
|
||||
// One of our parent models has enabled forceId because
|
||||
// it uses an auto-generated id property. However,
|
||||
// this particular model does not use auto-generated id,
|
||||
// therefore we need to disable `forceId`.
|
||||
forceId = false;
|
||||
}
|
||||
|
||||
if (forceId) {
|
||||
ModelClass.validatesAbsenceOf(idName, {if: 'isNewRecord'});
|
||||
}
|
||||
|
||||
ModelClass.definition.properties[idName].updateOnly = !!forceId;
|
||||
ModelClass.definition.rawProperties[idName].updateOnly = !!forceId;
|
||||
|
||||
ModelClass.settings.forceId = forceId;
|
||||
}
|
||||
|
||||
// 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++) {
|
||||
ModelClass.forEachProperty = function(cb) {
|
||||
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]]);
|
||||
}
|
||||
};
|
||||
|
||||
// A function to attach the model class to a data source
|
||||
ModelClass.attachTo = function (dataSource) {
|
||||
ModelClass.attachTo = function(dataSource) {
|
||||
dataSource.attach(this);
|
||||
};
|
||||
|
||||
|
@ -347,59 +395,61 @@ ModelBuilder.prototype.define = function defineClass(className, properties, sett
|
|||
* ```
|
||||
*
|
||||
* @param {String} className Name of the new model being defined.
|
||||
* @options {Object} properties Properties to define for the model, added to properties of model being extended.
|
||||
* @options {Object} settings Model settings, such as relations and acls.
|
||||
*
|
||||
* @options {Object} subClassProperties child model properties, added to base model
|
||||
* properties.
|
||||
* @options {Object} subClassSettings child model settings such as relations and acls,
|
||||
* merged with base model settings.
|
||||
*/
|
||||
ModelClass.extend = function (className, subclassProperties, subclassSettings) {
|
||||
var properties = ModelClass.definition.properties;
|
||||
var settings = ModelClass.definition.settings;
|
||||
ModelClass.extend = function(className, subClassProperties, subClassSettings) {
|
||||
const baseClassProperties = ModelClass.definition.properties;
|
||||
const baseClassSettings = ModelClass.definition.settings;
|
||||
|
||||
subclassProperties = subclassProperties || {};
|
||||
subclassSettings = subclassSettings || {};
|
||||
subClassProperties = subClassProperties || {};
|
||||
subClassSettings = subClassSettings || {};
|
||||
|
||||
// Check if subclass redefines the ids
|
||||
var idFound = false;
|
||||
for (var k in subclassProperties) {
|
||||
if (subclassProperties[k] && subclassProperties[k].id) {
|
||||
let idFound = false;
|
||||
for (const k in subClassProperties) {
|
||||
if (subClassProperties[k] && subClassProperties[k].id) {
|
||||
idFound = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Merging the properties
|
||||
var keys = Object.keys(properties);
|
||||
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 && properties[key].id) {
|
||||
if (idFound && baseClassProperties[key].id) {
|
||||
// don't inherit id properties
|
||||
continue;
|
||||
}
|
||||
if (subclassProperties[key] === undefined) {
|
||||
var baseProp = properties[key];
|
||||
var basePropCopy = baseProp;
|
||||
if (subClassProperties[key] === undefined) {
|
||||
const baseProp = baseClassProperties[key];
|
||||
let basePropCopy = baseProp;
|
||||
if (baseProp && typeof baseProp === 'object') {
|
||||
// Deep clone the base prop
|
||||
basePropCopy = mergeSettings(null, baseProp);
|
||||
// Deep clone the base properties
|
||||
basePropCopy = deepMerge(baseProp);
|
||||
}
|
||||
subclassProperties[key] = basePropCopy;
|
||||
subClassProperties[key] = basePropCopy;
|
||||
}
|
||||
}
|
||||
|
||||
// Merge the settings
|
||||
var originalSubclassSettings = subclassSettings;
|
||||
subclassSettings = mergeSettings(settings, subclassSettings);
|
||||
// Merging the settings
|
||||
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'
|
||||
// as that is removed from settings by modelBuilder.define and thus
|
||||
// it is never inherited
|
||||
if (!originalSubclassSettings.base) {
|
||||
subclassSettings.base = ModelClass;
|
||||
subClassSettings.base = ModelClass;
|
||||
}
|
||||
|
||||
// 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') {
|
||||
|
@ -409,33 +459,120 @@ ModelBuilder.prototype.define = function defineClass(className, properties, sett
|
|||
return subClass;
|
||||
};
|
||||
|
||||
/*
|
||||
* Merge parent and child model settings according to the provided merge policy.
|
||||
*
|
||||
* Below is presented the expected merge behaviour for each option of the policy.
|
||||
* NOTE: This applies to top-level settings properties
|
||||
*
|
||||
* - Any
|
||||
* - `{replace: true}` (default): child replaces the value from parent
|
||||
* - assigning `null` on child setting deletes the inherited setting
|
||||
*
|
||||
* - Arrays:
|
||||
* - `{replace: false}`: unique elements of parent and child cumulate
|
||||
* - `{rank: true}` adds the model inheritance rank to array
|
||||
* elements of type Object {} as internal property `__rank`
|
||||
*
|
||||
* - Object {}:
|
||||
* - `{replace: false}`: deep merges parent and child objects
|
||||
* - `{patch: true}`: child replaces inner properties from parent
|
||||
*
|
||||
* Here is an example of merge policy:
|
||||
* ```
|
||||
* {
|
||||
* description: {replace: true}, // string or array
|
||||
* properties: {patch: true}, // object
|
||||
* hidden: {replace: false}, // array
|
||||
* protected: {replace: false}, // array
|
||||
* relations: {acls: true}, // object
|
||||
* acls: {rank: true}, // array
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* @param {Object} baseClassSettings parent model settings.
|
||||
* @param {Object} subClassSettings child model settings.
|
||||
* @param {Object} mergePolicy merge policy, as defined in `ModelClass.getMergePolicy()`
|
||||
* @return {Object} mergedSettings merged parent and child models settings.
|
||||
*/
|
||||
function mergeSettings(baseClassSettings, subClassSettings, mergePolicy) {
|
||||
// deep clone base class settings
|
||||
const mergedSettings = deepMerge(baseClassSettings);
|
||||
|
||||
Object.keys(baseClassSettings).forEach(function(key) {
|
||||
// rank base class settings arrays elements where required
|
||||
if (mergePolicy[key] && mergePolicy[key].rank) {
|
||||
baseClassSettings[key] = rankArrayElements(baseClassSettings[key], ModelBaseClass.__rank);
|
||||
}
|
||||
});
|
||||
|
||||
Object.keys(subClassSettings).forEach(function(key) {
|
||||
// assign default merge policy to unknown settings if specified
|
||||
// if none specified, a deep merge will be applied eventually
|
||||
if (mergePolicy[key] == null) { // undefined or null
|
||||
mergePolicy[key] = mergePolicy.__default || {};
|
||||
}
|
||||
|
||||
// allow null value to remove unwanted settings from base class settings
|
||||
if (subClassSettings[key] === mergePolicy.__delete) {
|
||||
delete mergedSettings[key];
|
||||
return;
|
||||
}
|
||||
// rank sub class settings arrays elements where required
|
||||
if (mergePolicy[key].rank) {
|
||||
subClassSettings[key] = rankArrayElements(subClassSettings[key], ModelBaseClass.__rank + 1);
|
||||
}
|
||||
// replace base class settings where required
|
||||
if (mergePolicy[key].replace) {
|
||||
mergedSettings[key] = subClassSettings[key];
|
||||
return;
|
||||
}
|
||||
// patch inner properties of base class settings where required
|
||||
if (mergePolicy[key].patch) {
|
||||
// mergedSettings[key] might not be initialized
|
||||
mergedSettings[key] = mergedSettings[key] || {};
|
||||
Object.keys(subClassSettings[key]).forEach(function(innerKey) {
|
||||
mergedSettings[key][innerKey] = subClassSettings[key][innerKey];
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
// in case no merge policy matched, apply a deep merge
|
||||
// this for example handles {replace: false} and {rank: true}
|
||||
mergedSettings[key] = deepMergeProperty(baseClassSettings[key], subClassSettings[key]);
|
||||
});
|
||||
|
||||
return mergedSettings;
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a property for the model class
|
||||
* @param {String} propertyName Name of the property.
|
||||
*/
|
||||
ModelClass.registerProperty = function (propertyName) {
|
||||
var properties = modelDefinition.build();
|
||||
var prop = properties[propertyName];
|
||||
var DataType = prop.type;
|
||||
ModelClass.registerProperty = function(propertyName) {
|
||||
const properties = modelDefinition.build();
|
||||
const prop = properties[propertyName];
|
||||
const DataType = prop.type;
|
||||
if (!DataType) {
|
||||
throw new Error('Invalid type for property ' + propertyName);
|
||||
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);
|
||||
|
||||
Object.defineProperty(ModelClass.prototype, propertyName, {
|
||||
get: function () {
|
||||
get: function() {
|
||||
if (ModelClass.getter[propertyName]) {
|
||||
return ModelClass.getter[propertyName].call(this); // Try getter first
|
||||
} else {
|
||||
return this.__data && this.__data[propertyName]; // Try __data
|
||||
}
|
||||
},
|
||||
set: function (value) {
|
||||
var DataType = ModelClass.definition.properties[propertyName].type;
|
||||
set: function(value) {
|
||||
let DataType = ModelClass.definition.properties[propertyName].type;
|
||||
if (Array.isArray(DataType) || DataType === Array) {
|
||||
DataType = List;
|
||||
} else if (DataType === Date) {
|
||||
|
@ -446,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;
|
||||
}
|
||||
|
@ -459,50 +596,58 @@ 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
configurable: true,
|
||||
enumerable: true
|
||||
enumerable: true,
|
||||
});
|
||||
|
||||
// FIXME: [rfeng] Do we need to keep the raw data?
|
||||
// Use $ as the prefix to avoid conflicts with properties such as _id
|
||||
Object.defineProperty(ModelClass.prototype, '$' + propertyName, {
|
||||
get: function () {
|
||||
get: function() {
|
||||
return this.__data && this.__data[propertyName];
|
||||
},
|
||||
set: function (value) {
|
||||
set: function(value) {
|
||||
if (!this.__data) {
|
||||
this.__data = {};
|
||||
}
|
||||
this.__data[propertyName] = value;
|
||||
},
|
||||
configurable: true,
|
||||
enumerable: false
|
||||
enumerable: false,
|
||||
});
|
||||
};
|
||||
|
||||
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 = {};
|
||||
}
|
||||
|
@ -521,15 +666,50 @@ ModelBuilder.prototype.define = function defineClass(className, properties, sett
|
|||
ModelClass.emit('defined', ModelClass);
|
||||
|
||||
return ModelClass;
|
||||
|
||||
};
|
||||
|
||||
function createModelClassCtor(name, ModelBaseClass) {
|
||||
// A simple sanitization to handle most common characters
|
||||
// that are used in model names but cannot be used as a function/class name.
|
||||
// Note that the rules for valid JS indentifiers are way too complex,
|
||||
// implementing a fully spec-compliant sanitization is not worth the effort.
|
||||
// See https://mathiasbynens.be/notes/javascript-identifiers-es6
|
||||
name = name.replace(/[-.:]/g, '_');
|
||||
|
||||
try {
|
||||
// It is not possible to access closure variables like "ModelBaseClass"
|
||||
// from a dynamically defined function. The solution is to
|
||||
// create a dynamically defined factory function that accepts
|
||||
// closure variables as arguments.
|
||||
const factory = new Function('ModelBaseClass', `
|
||||
// every class can receive hash of data as optional param
|
||||
return function ${name}(data, options) {
|
||||
if (!(this instanceof ${name})) {
|
||||
return new ${name}(data, options);
|
||||
}
|
||||
if (${name}.settings.unresolved) {
|
||||
throw new Error(g.f('Model %s is not defined.', ${JSON.stringify(name)}));
|
||||
}
|
||||
ModelBaseClass.apply(this, arguments);
|
||||
};`);
|
||||
|
||||
return factory(ModelBaseClass);
|
||||
} catch (err) {
|
||||
// modelName is not a valid function/class name, e.g. 'grand-child'
|
||||
// and our simple sanitization was not good enough.
|
||||
// Falling back to legacy 'ModelConstructor' name.
|
||||
if (err.name === 'SyntaxError') {
|
||||
return createModelClassCtor('ModelConstructor', ModelBaseClass);
|
||||
} else {
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// DataType for Date
|
||||
function DateType(arg) {
|
||||
var d = new Date(arg);
|
||||
if (isNaN(d.getTime())) {
|
||||
throw new Error('Invalid date: ' + arg);
|
||||
}
|
||||
if (arg === null) return null;
|
||||
const d = new Date(arg);
|
||||
return d;
|
||||
}
|
||||
|
||||
|
@ -558,7 +738,7 @@ function BooleanType(arg) {
|
|||
* @param {String} propertyName Name of property
|
||||
* @param {Object} propertyDefinition Property settings
|
||||
*/
|
||||
ModelBuilder.prototype.defineProperty = function (model, propertyName, propertyDefinition) {
|
||||
ModelBuilder.prototype.defineProperty = function(model, propertyName, propertyDefinition) {
|
||||
this.definitions[model].defineProperty(propertyName, propertyDefinition);
|
||||
this.models[model].registerProperty(propertyName);
|
||||
};
|
||||
|
@ -602,20 +782,20 @@ ModelBuilder.prototype.defineValueType = function(type, aliases) {
|
|||
* @property {String} type Datatype of property: Must be an [LDL type](http://docs.strongloop.com/display/LB/LoopBack+types).
|
||||
* @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]];
|
||||
ModelBuilder.prototype.extendModel = function(model, props) {
|
||||
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));
|
||||
};
|
||||
|
||||
|
@ -628,18 +808,27 @@ ModelBuilder.prototype.copyModel = function copyModel(Master) {
|
|||
hiddenProperty(Slave, 'relations', Master.relations);
|
||||
|
||||
if (!(className in modelBuilder.models)) {
|
||||
|
||||
// store class in model pool
|
||||
modelBuilder.models[className] = Slave;
|
||||
modelBuilder.definitions[className] = {
|
||||
properties: md.properties,
|
||||
settings: md.settings
|
||||
settings: md.settings,
|
||||
};
|
||||
}
|
||||
|
||||
return Slave;
|
||||
};
|
||||
|
||||
/**
|
||||
* Remove a model from the registry.
|
||||
*
|
||||
* @param {String} modelName
|
||||
*/
|
||||
ModelBuilder.prototype.deleteModelByName = function(modelName) {
|
||||
delete this.models[modelName];
|
||||
delete this.definitions[modelName];
|
||||
};
|
||||
|
||||
/*!
|
||||
* Define hidden property
|
||||
*/
|
||||
|
@ -648,14 +837,17 @@ function hiddenProperty(where, property, value) {
|
|||
writable: true,
|
||||
enumerable: false,
|
||||
configurable: true,
|
||||
value: value
|
||||
value: value,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the schema name
|
||||
* Get the schema name. If no parameter is given, then an anonymous model name
|
||||
* is generated and returned.
|
||||
* @param {string=} name The optional name parameter.
|
||||
* @returns {string} The schema name.
|
||||
*/
|
||||
ModelBuilder.prototype.getSchemaName = function (name) {
|
||||
ModelBuilder.prototype.getSchemaName = function(name) {
|
||||
if (name) {
|
||||
return name;
|
||||
}
|
||||
|
@ -670,43 +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'. It's 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 {
|
||||
} 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, {anonymous: true, idInjection: false});
|
||||
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;
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -719,10 +914,11 @@ ModelBuilder.prototype.resolveType = function (type) {
|
|||
* 3. A list of property definitions (anonymous)
|
||||
*
|
||||
* @param {*} schemas The schemas
|
||||
* @returns {Object} A map of model constructors keyed by model name
|
||||
* @returns {Object.<string, ModelClass>} A map of model constructors keyed by
|
||||
* model name.
|
||||
*/
|
||||
ModelBuilder.prototype.buildModels = function (schemas, createModel) {
|
||||
var models = {};
|
||||
ModelBuilder.prototype.buildModels = function(schemas, createModel) {
|
||||
const models = {};
|
||||
|
||||
// Normalize the schemas to be an array of the schema objects {name: <name>, properties: {}, options: {}}
|
||||
if (!Array.isArray(schemas)) {
|
||||
|
@ -735,31 +931,28 @@ ModelBuilder.prototype.buildModels = function (schemas, createModel) {
|
|||
{
|
||||
name: this.getSchemaName(),
|
||||
properties: schemas,
|
||||
options: {anonymous: true}
|
||||
}
|
||||
options: {anonymous: true},
|
||||
},
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
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});
|
||||
|
@ -774,16 +967,12 @@ ModelBuilder.prototype.buildModels = function (schemas, createModel) {
|
|||
* @param {String} name The model name
|
||||
* @param {Object} json The JSON object
|
||||
* @param {Object} options The options
|
||||
* @returns {}
|
||||
* @returns {ModelClass} The generated model class constructor.
|
||||
*/
|
||||
ModelBuilder.prototype.buildModelFromInstance = function (name, json, options) {
|
||||
|
||||
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,9 +1,16 @@
|
|||
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');
|
||||
// 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';
|
||||
|
||||
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
|
||||
|
@ -29,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 || {};
|
||||
|
@ -53,8 +60,8 @@ require('./types')(ModelDefinition);
|
|||
* Return table name for specified `modelName`
|
||||
* @param {String} connectorType The connector type, such as 'oracle' or 'mongodb'
|
||||
*/
|
||||
ModelDefinition.prototype.tableName = function (connectorType) {
|
||||
var settings = this.settings;
|
||||
ModelDefinition.prototype.tableName = function(connectorType) {
|
||||
const settings = this.settings;
|
||||
if (settings[connectorType]) {
|
||||
return settings[connectorType].table || settings[connectorType].tableName || this.name;
|
||||
} else {
|
||||
|
@ -68,12 +75,12 @@ ModelDefinition.prototype.tableName = function (connectorType) {
|
|||
* @param propertyName The property name
|
||||
* @returns {String} columnName
|
||||
*/
|
||||
ModelDefinition.prototype.columnName = function (connectorType, propertyName) {
|
||||
ModelDefinition.prototype.columnName = function(connectorType, propertyName) {
|
||||
if (!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 {
|
||||
|
@ -87,12 +94,12 @@ ModelDefinition.prototype.columnName = function (connectorType, propertyName) {
|
|||
* @param propertyName The property name
|
||||
* @returns {Object} column metadata
|
||||
*/
|
||||
ModelDefinition.prototype.columnMetadata = function (connectorType, propertyName) {
|
||||
ModelDefinition.prototype.columnMetadata = function(connectorType, propertyName) {
|
||||
if (!propertyName) {
|
||||
return propertyName;
|
||||
}
|
||||
this.build();
|
||||
var property = this.properties[propertyName];
|
||||
const property = this.properties[propertyName];
|
||||
if (property && property[connectorType]) {
|
||||
return property[connectorType];
|
||||
} else {
|
||||
|
@ -105,13 +112,13 @@ ModelDefinition.prototype.columnMetadata = function (connectorType, propertyName
|
|||
* @param {String} connectorType The connector type, such as 'oracle' or 'mongodb'
|
||||
* @returns {String[]} column names
|
||||
*/
|
||||
ModelDefinition.prototype.columnNames = function (connectorType) {
|
||||
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[connectorType].column || props[p][connectorType].columnName || p);
|
||||
cols.push(props[p][connectorType].column || props[p][connectorType].columnName || p);
|
||||
} else {
|
||||
cols.push(p);
|
||||
}
|
||||
|
@ -123,15 +130,15 @@ ModelDefinition.prototype.columnNames = function (connectorType) {
|
|||
* Find the ID properties sorted by the index
|
||||
* @returns {Object[]} property name/index for IDs
|
||||
*/
|
||||
ModelDefinition.prototype.ids = function () {
|
||||
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;
|
||||
}
|
||||
|
@ -140,7 +147,7 @@ ModelDefinition.prototype.ids = function () {
|
|||
}
|
||||
ids.push({name: key, id: id, property: props[key]});
|
||||
}
|
||||
ids.sort(function (a, b) {
|
||||
ids.sort(function(a, b) {
|
||||
return a.id - b.id;
|
||||
});
|
||||
this._ids = ids;
|
||||
|
@ -152,7 +159,7 @@ ModelDefinition.prototype.ids = function () {
|
|||
* @param {String} modelName The model name
|
||||
* @returns {String} columnName for ID
|
||||
*/
|
||||
ModelDefinition.prototype.idColumnName = function (connectorType) {
|
||||
ModelDefinition.prototype.idColumnName = function(connectorType) {
|
||||
return this.columnName(connectorType, this.idName());
|
||||
};
|
||||
|
||||
|
@ -160,8 +167,8 @@ ModelDefinition.prototype.idColumnName = function (connectorType) {
|
|||
* Find the ID property name
|
||||
* @returns {String} property name for ID
|
||||
*/
|
||||
ModelDefinition.prototype.idName = function () {
|
||||
var id = this.ids()[0];
|
||||
ModelDefinition.prototype.idName = function() {
|
||||
const id = this.ids()[0];
|
||||
if (this.properties.id && this.properties.id.id) {
|
||||
return 'id';
|
||||
} else {
|
||||
|
@ -173,9 +180,9 @@ ModelDefinition.prototype.idName = function () {
|
|||
* Find the ID property names sorted by the index
|
||||
* @returns {String[]} property names for IDs
|
||||
*/
|
||||
ModelDefinition.prototype.idNames = function () {
|
||||
var ids = this.ids();
|
||||
var names = ids.map(function (id) {
|
||||
ModelDefinition.prototype.idNames = function() {
|
||||
const ids = this.ids();
|
||||
const names = ids.map(function(id) {
|
||||
return id.name;
|
||||
});
|
||||
return names;
|
||||
|
@ -185,15 +192,15 @@ ModelDefinition.prototype.idNames = function () {
|
|||
*
|
||||
* @returns {{}}
|
||||
*/
|
||||
ModelDefinition.prototype.indexes = 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;
|
||||
}
|
||||
|
@ -205,7 +212,7 @@ ModelDefinition.prototype.indexes = function () {
|
|||
* Build a model definition
|
||||
* @param {Boolean} force Forcing rebuild
|
||||
*/
|
||||
ModelDefinition.prototype.build = function (forceRebuild) {
|
||||
ModelDefinition.prototype.build = function(forceRebuild) {
|
||||
if (forceRebuild) {
|
||||
this.properties = null;
|
||||
this.relations = [];
|
||||
|
@ -216,22 +223,22 @@ 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,
|
||||
target: type,
|
||||
type: Array.isArray(prop) ? 'hasMany' : 'belongsTo',
|
||||
as: p
|
||||
as: p,
|
||||
});
|
||||
} else {
|
||||
var typeDef = {
|
||||
type: type
|
||||
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];
|
||||
|
@ -249,7 +256,7 @@ ModelDefinition.prototype.build = function (forceRebuild) {
|
|||
* @param {String} propertyName The property name
|
||||
* @param {Object} propertyDefinition The property definition
|
||||
*/
|
||||
ModelDefinition.prototype.defineProperty = function (propertyName, propertyDefinition) {
|
||||
ModelDefinition.prototype.defineProperty = function(propertyName, propertyDefinition) {
|
||||
this.rawProperties[propertyName] = propertyDefinition;
|
||||
this.build(true);
|
||||
};
|
||||
|
@ -261,21 +268,21 @@ function isModelClass(cls) {
|
|||
return cls.prototype instanceof ModelBaseClass;
|
||||
}
|
||||
|
||||
ModelDefinition.prototype.toJSON = function (forceRebuild) {
|
||||
ModelDefinition.prototype.toJSON = function(forceRebuild) {
|
||||
if (forceRebuild) {
|
||||
this.json = null;
|
||||
}
|
||||
if (this.json) {
|
||||
return this.json;
|
||||
}
|
||||
var json = {
|
||||
const json = {
|
||||
name: this.name,
|
||||
properties: {},
|
||||
settings: this.settings
|
||||
settings: this.settings,
|
||||
};
|
||||
this.build(forceRebuild);
|
||||
|
||||
var mapper = function (val) {
|
||||
const mapper = function(val) {
|
||||
if (val === undefined || val === null) {
|
||||
return val;
|
||||
}
|
||||
|
@ -296,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');
|
||||
}
|
||||
|
526
lib/model.js
526
lib/model.js
|
@ -1,3 +1,12 @@
|
|||
// 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
|
||||
|
||||
// Turning on strict for this file breaks lots of test cases;
|
||||
// disabling strict for this file
|
||||
/* eslint-disable strict */
|
||||
|
||||
/*!
|
||||
* Module exports class Model
|
||||
*/
|
||||
|
@ -7,26 +16,28 @@ module.exports = ModelBaseClass;
|
|||
* Module dependencies
|
||||
*/
|
||||
|
||||
var async = require('async');
|
||||
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('node-uuid');
|
||||
var deprecated = require('depd')('loopback-datasource-juggler');
|
||||
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,
|
||||
'Date': true,
|
||||
'Text': true,
|
||||
'ObjectID': true
|
||||
'ObjectID': true,
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -36,10 +47,12 @@ var BASE_TYPES = {
|
|||
*
|
||||
* @class
|
||||
* @param {Object} data Initial object data
|
||||
* @param {Object} options An object to control the instantiation
|
||||
* @returns {ModelBaseClass} an instance of the ModelBaseClass
|
||||
*/
|
||||
function ModelBaseClass(data, options) {
|
||||
options = options || {};
|
||||
if(!('applySetters' in options)) {
|
||||
if (!('applySetters' in options)) {
|
||||
// Default to true
|
||||
options.applySetters = true;
|
||||
}
|
||||
|
@ -59,15 +72,20 @@ function ModelBaseClass(data, options) {
|
|||
* @property {Boolean} persisted Whether the instance has been persisted
|
||||
* @private
|
||||
*/
|
||||
ModelBaseClass.prototype._initProperties = function (data, options) {
|
||||
var self = this;
|
||||
var ctor = this.constructor;
|
||||
ModelBaseClass.prototype._initProperties = function(data, options) {
|
||||
const self = this;
|
||||
const ctor = this.constructor;
|
||||
|
||||
if(data instanceof ctor) {
|
||||
if (typeof data !== 'undefined' && data !== null && data.constructor &&
|
||||
typeof (data.constructor) !== 'function') {
|
||||
throw new Error(g.f('Property name "{{constructor}}" is not allowed in %s data', ctor.modelName));
|
||||
}
|
||||
|
||||
if (data instanceof ctor) {
|
||||
// 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') {
|
||||
|
@ -75,15 +93,19 @@ 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) {
|
||||
if (strict === undefined) {
|
||||
strict = ctor.definition.settings.strict;
|
||||
} else if (strict === 'throw') {
|
||||
g.warn('Warning: Model %s, {{strict mode: `throw`}} has been removed, ' +
|
||||
'please use {{`strict: true`}} instead, which returns' +
|
||||
'{{`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
|
||||
|
@ -94,14 +116,14 @@ ModelBaseClass.prototype._initProperties = function (data, options) {
|
|||
writable: true,
|
||||
enumerable: false,
|
||||
configurable: true,
|
||||
value: {}
|
||||
value: {},
|
||||
},
|
||||
|
||||
__data: {
|
||||
writable: true,
|
||||
enumerable: false,
|
||||
configurable: true,
|
||||
value: {}
|
||||
value: {},
|
||||
},
|
||||
|
||||
// Instance level data source
|
||||
|
@ -109,7 +131,7 @@ ModelBaseClass.prototype._initProperties = function (data, options) {
|
|||
writable: true,
|
||||
enumerable: false,
|
||||
configurable: true,
|
||||
value: options.dataSource
|
||||
value: options.dataSource,
|
||||
},
|
||||
|
||||
// Instance level strict mode
|
||||
|
@ -117,23 +139,23 @@ ModelBaseClass.prototype._initProperties = function (data, options) {
|
|||
writable: true,
|
||||
enumerable: false,
|
||||
configurable: true,
|
||||
value: strict
|
||||
value: strict,
|
||||
},
|
||||
|
||||
__persisted: {
|
||||
writable: true,
|
||||
enumerable: false,
|
||||
configurable: true,
|
||||
value: false
|
||||
value: false,
|
||||
},
|
||||
});
|
||||
|
||||
if (strict === 'validate') {
|
||||
if (strict) {
|
||||
Object.defineProperty(this, '__unknownProperties', {
|
||||
writable: true,
|
||||
enumerable: false,
|
||||
configrable: true,
|
||||
value: []
|
||||
value: [],
|
||||
});
|
||||
}
|
||||
} else {
|
||||
|
@ -142,7 +164,7 @@ ModelBaseClass.prototype._initProperties = function (data, options) {
|
|||
this.__dataSource = options.dataSource;
|
||||
this.__strict = strict;
|
||||
this.__persisted = false;
|
||||
if (strict === 'validate') {
|
||||
if (strict) {
|
||||
this.__unknownProperties = [];
|
||||
}
|
||||
}
|
||||
|
@ -155,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) {
|
||||
|
@ -163,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') {
|
||||
|
@ -184,16 +206,18 @@ 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;
|
||||
properties[p] = { name: typeName, type: propType };
|
||||
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);
|
||||
*/
|
||||
}
|
||||
|
||||
// Relation
|
||||
|
@ -202,12 +226,16 @@ 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);
|
||||
}
|
||||
self.__data[p] = new modelTo(propVal, { fields: fields, applySetters: false, persisted: options.persisted });
|
||||
self.__data[p] = new modelTo(propVal, {
|
||||
fields: fields,
|
||||
applySetters: false,
|
||||
persisted: options.persisted,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -218,16 +246,18 @@ ModelBaseClass.prototype._initProperties = function (data, options) {
|
|||
self[p] = self.__data[p] =
|
||||
(propVal !== undefined) ? propVal : self.__cachedRelations[p];
|
||||
|
||||
// Warn about properties with unsupported names
|
||||
// Throw error for properties with unsupported names
|
||||
if (/\./.test(p)) {
|
||||
deprecated('Property names containing a dot are not supported. ' +
|
||||
'Model: ' + this.constructor.modelName +
|
||||
', dynamic property: ' + p);
|
||||
throw new Error(g.f(
|
||||
'Property names containing dot(s) are not supported. ' +
|
||||
'Model: %s, dynamic property: %s',
|
||||
this.constructor.modelName, p,
|
||||
));
|
||||
}
|
||||
} else {
|
||||
if (strict !== 'filter') {
|
||||
this.__unknownProperties.push(p);
|
||||
}
|
||||
} else if (strict === 'throw') {
|
||||
throw new Error('Unknown property: ' + p);
|
||||
} else if (strict === 'validate') {
|
||||
this.__unknownProperties.push(p);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -242,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) {
|
||||
|
@ -269,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;
|
||||
|
@ -287,9 +321,13 @@ ModelBaseClass.prototype._initProperties = function (data, options) {
|
|||
case 'now':
|
||||
propVal = new Date();
|
||||
break;
|
||||
case 'shortid':
|
||||
case 'nanoid':
|
||||
propVal = nanoid(9);
|
||||
break;
|
||||
default:
|
||||
// TODO Support user-provided functions via a registry of functions
|
||||
console.warn('Unknown default value provider ' + defn);
|
||||
g.warn('Unknown default value provider %s', defn);
|
||||
}
|
||||
// FIXME: We should coerce the value
|
||||
// will implement it after we refactor the PropertyDefinition
|
||||
|
@ -303,7 +341,6 @@ ModelBaseClass.prototype._initProperties = function (data, options) {
|
|||
|
||||
// Handle complex types (JSON/Object)
|
||||
if (!BASE_TYPES[type.name]) {
|
||||
|
||||
if (typeof self.__data[p] !== 'object' && self.__data[p]) {
|
||||
try {
|
||||
self.__data[p] = JSON.parse(self.__data[p] + '');
|
||||
|
@ -313,15 +350,16 @@ ModelBaseClass.prototype._initProperties = function (data, options) {
|
|||
}
|
||||
|
||||
if (type.prototype instanceof ModelBaseClass) {
|
||||
if (!(self.__data[p] instanceof type)
|
||||
&& typeof self.__data[p] === 'object'
|
||||
&& self.__data[p] !== null ) {
|
||||
if (!(self.__data[p] instanceof type) &&
|
||||
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)
|
||||
&& self.__data[p] !== undefined
|
||||
&& self.__data[p] !== null ) {
|
||||
if (!(self.__data[p] instanceof List) &&
|
||||
self.__data[p] !== undefined &&
|
||||
self.__data[p] !== null) {
|
||||
self.__data[p] = List(self.__data[p], type, self);
|
||||
}
|
||||
}
|
||||
|
@ -330,33 +368,58 @@ 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
|
||||
* @param {Object} params Various property configuration
|
||||
*/
|
||||
ModelBaseClass.defineProperty = function (prop, params) {
|
||||
if(this.dataSource) {
|
||||
ModelBaseClass.defineProperty = function(prop, params) {
|
||||
if (this.dataSource) {
|
||||
this.dataSource.defineProperty(this.modelName, prop, params);
|
||||
} else {
|
||||
this.modelBuilder.defineProperty(this.modelName, prop, params);
|
||||
}
|
||||
};
|
||||
|
||||
ModelBaseClass.getPropertyType = function (propName) {
|
||||
var prop = this.definition.properties[propName];
|
||||
/**
|
||||
* Get model property type.
|
||||
* @param {String} propName Property name
|
||||
* @returns {String} Name of property type
|
||||
*/
|
||||
ModelBaseClass.getPropertyType = function(propName) {
|
||||
const prop = this.definition.properties[propName];
|
||||
if (!prop) {
|
||||
// The property is not part of the definition
|
||||
return null;
|
||||
}
|
||||
if (!prop.type) {
|
||||
throw new Error('Type not defined for property ' + this.modelName + '.' + propName);
|
||||
throw new Error(g.f('Type not defined for property %s.%s', this.modelName, propName));
|
||||
// return null;
|
||||
}
|
||||
return prop.type.name;
|
||||
};
|
||||
|
||||
ModelBaseClass.prototype.getPropertyType = function (propName) {
|
||||
/**
|
||||
* Get model property type.
|
||||
* @param {String} propName Property name
|
||||
* @returns {String} Name of property type
|
||||
*/
|
||||
ModelBaseClass.prototype.getPropertyType = function(propName) {
|
||||
return this.constructor.getPropertyType(propName);
|
||||
};
|
||||
|
||||
|
@ -364,7 +427,7 @@ ModelBaseClass.prototype.getPropertyType = function (propName) {
|
|||
* Return string representation of class
|
||||
* This overrides the default `toString()` method
|
||||
*/
|
||||
ModelBaseClass.toString = function () {
|
||||
ModelBaseClass.toString = function() {
|
||||
return '[Model ' + this.modelName + ']';
|
||||
};
|
||||
|
||||
|
@ -373,29 +436,38 @@ ModelBaseClass.toString = function () {
|
|||
* Returns a canonical object representation (no getters and setters).
|
||||
*
|
||||
* @param {Boolean} onlySchema Restrict properties to dataSource only. Default is false. If true, the function returns only properties defined in the schema; Otherwise it returns all enumerable properties.
|
||||
* @param {Boolean} removeHidden Boolean flag as part of the transformation. If true, then hidden properties should not be brought out.
|
||||
* @param {Boolean} removeProtected Boolean flag as part of the transformation. If true, then protected properties should not be brought out.
|
||||
* @returns {Object} returns Plain JSON object
|
||||
*/
|
||||
ModelBaseClass.prototype.toObject = function (onlySchema, removeHidden, removeProtected) {
|
||||
ModelBaseClass.prototype.toObject = function(onlySchema, removeHidden, removeProtected) {
|
||||
if (typeof onlySchema === 'object' && onlySchema != null) {
|
||||
const options = onlySchema;
|
||||
onlySchema = options.onlySchema;
|
||||
removeHidden = options.removeHidden;
|
||||
removeProtected = options.removeProtected;
|
||||
}
|
||||
if (onlySchema === undefined) {
|
||||
onlySchema = true;
|
||||
}
|
||||
var data = {};
|
||||
var self = this;
|
||||
var Model = this.constructor;
|
||||
const data = {};
|
||||
const self = this;
|
||||
const Model = this.constructor;
|
||||
|
||||
// if it is already an Object
|
||||
if (Model === Object) {
|
||||
return self;
|
||||
}
|
||||
|
||||
var strict = this.__strict;
|
||||
var schemaLess = (strict === false) || !onlySchema;
|
||||
var persistUndefinedAsNull = Model.definition.settings.persistUndefinedAsNull;
|
||||
const strict = this.__strict;
|
||||
const schemaLess = (strict === false) || !onlySchema;
|
||||
const persistUndefinedAsNull = Model.definition.settings.persistUndefinedAsNull;
|
||||
|
||||
var props = Model.definition.properties;
|
||||
var keys = Object.keys(props);
|
||||
var propertyName, val;
|
||||
const props = Model.definition.properties;
|
||||
let keys = Object.keys(props);
|
||||
let propertyName, val;
|
||||
|
||||
for (var i = 0; i < keys.length; i++) {
|
||||
for (let i = 0; i < keys.length; i++) {
|
||||
propertyName = keys[i];
|
||||
val = self[propertyName];
|
||||
|
||||
|
@ -431,8 +503,8 @@ ModelBaseClass.prototype.toObject = function (onlySchema, removeHidden, removePr
|
|||
// 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;
|
||||
|
@ -466,7 +538,7 @@ ModelBaseClass.prototype.toObject = function (onlySchema, removeHidden, removePr
|
|||
// 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;
|
||||
|
@ -478,7 +550,7 @@ ModelBaseClass.prototype.toObject = function (onlySchema, removeHidden, removePr
|
|||
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') {
|
||||
|
@ -499,50 +571,50 @@ ModelBaseClass.prototype.toObject = function (onlySchema, removeHidden, removePr
|
|||
return data;
|
||||
};
|
||||
|
||||
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;
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
protectedProperties = settings.protectedProperties;
|
||||
}
|
||||
if (protectedProperties) {
|
||||
return protectedProperties[propertyName];
|
||||
} else {
|
||||
return false;
|
||||
return obj;
|
||||
}
|
||||
return arr || obj;
|
||||
}
|
||||
/**
|
||||
* Checks if property is protected.
|
||||
* @param {String} propertyName Property name
|
||||
* @returns {Boolean} true or false if protected or not.
|
||||
*/
|
||||
ModelBaseClass.isProtectedProperty = function(propertyName) {
|
||||
const settings = (this.definition && this.definition.settings) || {};
|
||||
const protectedProperties = settings.protectedProperties || settings.protected;
|
||||
settings.protectedProperties = asObjectMap(protectedProperties);
|
||||
return settings.protectedProperties[propertyName];
|
||||
};
|
||||
|
||||
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;
|
||||
}
|
||||
/**
|
||||
* Checks if property is hidden.
|
||||
* @param {String} propertyName Property name
|
||||
* @returns {Boolean} true or false if hidden or not.
|
||||
*/
|
||||
ModelBaseClass.isHiddenProperty = function(propertyName) {
|
||||
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 () {
|
||||
ModelBaseClass.prototype.toJSON = function() {
|
||||
return this.toObject(false, true, false);
|
||||
};
|
||||
|
||||
ModelBaseClass.prototype.fromObject = function (obj) {
|
||||
for (var key in obj) {
|
||||
ModelBaseClass.prototype.fromObject = function(obj) {
|
||||
for (const key in obj) {
|
||||
this[key] = obj[key];
|
||||
}
|
||||
};
|
||||
|
@ -552,9 +624,9 @@ ModelBaseClass.prototype.fromObject = function (obj) {
|
|||
* This method does not perform any database operations; it just resets the object to its
|
||||
* initial state.
|
||||
*/
|
||||
ModelBaseClass.prototype.reset = function () {
|
||||
var obj = this;
|
||||
for (var k in obj) {
|
||||
ModelBaseClass.prototype.reset = function() {
|
||||
const obj = this;
|
||||
for (const k in obj) {
|
||||
if (k !== 'id' && !obj.constructor.dataSource.definitions[obj.constructor.modelName].properties[k]) {
|
||||
delete obj[k];
|
||||
}
|
||||
|
@ -564,35 +636,48 @@ 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);
|
||||
|
||||
ModelBaseClass.prototype.inspect = function (depth) {
|
||||
ModelBaseClass.prototype.inspect = function(depth) {
|
||||
if (INSPECT_SUPPORTS_OBJECT_RETVAL)
|
||||
return this.__data;
|
||||
return this.__data;
|
||||
|
||||
// Workaround for older versions
|
||||
// See also https://github.com/joyent/node/commit/66280de133
|
||||
return util.inspect(this.__data, {
|
||||
showHidden: false,
|
||||
depth: depth,
|
||||
colors: false
|
||||
colors: false,
|
||||
});
|
||||
};
|
||||
|
||||
ModelBaseClass.mixin = function (anotherClass, options) {
|
||||
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
|
||||
* @param {Object} options An object to control the instantiation
|
||||
* @returns {ModelClass}
|
||||
*/
|
||||
ModelBaseClass.mixin = function(anotherClass, options) {
|
||||
if (typeof anotherClass === 'string') {
|
||||
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;
|
||||
}
|
||||
|
@ -603,20 +688,187 @@ ModelBaseClass.mixin = function (anotherClass, options) {
|
|||
}
|
||||
};
|
||||
|
||||
ModelBaseClass.prototype.getDataSource = function () {
|
||||
ModelBaseClass.prototype.getDataSource = function() {
|
||||
return this.__dataSource || this.constructor.dataSource;
|
||||
};
|
||||
|
||||
ModelBaseClass.getDataSource = function () {
|
||||
ModelBaseClass.getDataSource = function() {
|
||||
return this.dataSource;
|
||||
};
|
||||
|
||||
ModelBaseClass.prototype.setStrict = function (strict) {
|
||||
ModelBaseClass.prototype.setStrict = function(strict) {
|
||||
this.__strict = strict;
|
||||
};
|
||||
|
||||
/**
|
||||
*
|
||||
* `getMergePolicy()` provides model merge policies to apply when extending
|
||||
* a child model from a base model. Such a policy drives the way parent/child model
|
||||
* properties/settings are merged/mixed-in together.
|
||||
*
|
||||
* Below is presented the expected merge behaviour for each option.
|
||||
* NOTE: This applies to top-level settings properties
|
||||
*
|
||||
*
|
||||
* - Any
|
||||
* - `{replace: true}` (default): child replaces the value from parent
|
||||
* - assignin `null` on child setting deletes the inherited setting
|
||||
*
|
||||
* - Arrays:
|
||||
* - `{replace: false}`: unique elements of parent and child cumulate
|
||||
* - `{rank: true}` adds the model inheritance rank to array
|
||||
* elements of type Object {} as internal property `__rank`
|
||||
*
|
||||
* - Object {}:
|
||||
* - `{replace: false}`: deep merges parent and child objects
|
||||
* - `{patch: true}`: child replaces inner properties from parent
|
||||
*
|
||||
*
|
||||
* The recommended built-in merge policy is as follows. It is returned by getMergePolicy()
|
||||
* when calling the method with option `{configureModelMerge: true}`.
|
||||
*
|
||||
* ```
|
||||
* {
|
||||
* description: {replace: true}, // string or array
|
||||
* options: {patch: true}, // object
|
||||
* hidden: {replace: false}, // array
|
||||
* protected: {replace: false}, // array
|
||||
* indexes: {patch: true}, // object
|
||||
* methods: {patch: true}, // object
|
||||
* mixins: {patch: true}, // object
|
||||
* relations: {patch: true}, // object
|
||||
* scope: {replace: true}, // object
|
||||
* scopes: {patch: true}, // object
|
||||
* acls: {rank: true}, // array
|
||||
* // this setting controls which child model property's value allows deleting
|
||||
* // a base model's property
|
||||
* __delete: null,
|
||||
* // this setting controls the default merge behaviour for settings not defined
|
||||
* // in the mergePolicy specification
|
||||
* __default: {replace: true},
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* The legacy built-in merge policy is as follows, it is retuned by `getMergePolicy()`
|
||||
* when avoiding option `configureModelMerge`.
|
||||
* NOTE: it also provides the ACLs ranking in addition to the legacy behaviour, as well
|
||||
* as fixes for settings 'description' and 'relations': matching relations from child
|
||||
* replace relations from parents.
|
||||
*
|
||||
* ```
|
||||
* {
|
||||
* description: {replace: true}, // string or array
|
||||
* properties: {patch: true}, // object
|
||||
* hidden: {replace: false}, // array
|
||||
* protected: {replace: false}, // array
|
||||
* relations: {acls: true}, // object
|
||||
* acls: {rank: true}, // array
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
*
|
||||
* `getMergePolicy()` can be customized using model's setting `configureModelMerge` as follows:
|
||||
*
|
||||
* ``` json
|
||||
* {
|
||||
* // ..
|
||||
* options: {
|
||||
* configureModelMerge: {
|
||||
* // merge options
|
||||
* }
|
||||
* }
|
||||
* // ..
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* NOTE: mergePolicy parameter can also defined at JSON model definition root
|
||||
*
|
||||
* `getMergePolicy()` method can also be extended programmatically as follows:
|
||||
*
|
||||
* ``` js
|
||||
* myModel.getMergePolicy = function(options) {
|
||||
* const origin = myModel.base.getMergePolicy(options);
|
||||
* return Object.assign({}, origin, {
|
||||
* // new/overriding options
|
||||
* });
|
||||
* };
|
||||
* ```
|
||||
*
|
||||
* @param {Object} options option `configureModelMerge` can be used to alter the
|
||||
* returned merge policy:
|
||||
* - `configureModelMerge: true` will have the method return the recommended merge policy.
|
||||
* - `configureModelMerge: {..}` will actually have the method return the provided object.
|
||||
* - not providing this options will have the method return a merge policy emulating the
|
||||
* the model merge behaviour up to datasource-juggler v3.6.1, as well as the ACLs ranking.
|
||||
* @returns {Object} mergePolicy The model merge policy to apply when using the
|
||||
* current model as base class for a child model
|
||||
*/
|
||||
ModelBaseClass.getMergePolicy = function(options) {
|
||||
// NOTE: merge policy equivalent to datasource-juggler behaviour up to v3.6.1
|
||||
// + fix for description arrays that should not be merged
|
||||
// + fix for relations that should patch matching relations
|
||||
// + ranking of ACLs
|
||||
let mergePolicy = {
|
||||
description: {replace: true}, // string or array
|
||||
properties: {patch: true}, // object
|
||||
hidden: {replace: false}, // array
|
||||
protected: {replace: false}, // array
|
||||
relations: {patch: true}, // object
|
||||
acls: {rank: true}, // array
|
||||
};
|
||||
|
||||
const config = (options || {}).configureModelMerge;
|
||||
|
||||
if (config === true) {
|
||||
// NOTE: recommended merge policy from datasource-juggler v3.6.2
|
||||
mergePolicy = {
|
||||
description: {replace: true}, // string or array
|
||||
options: {patch: true}, // object
|
||||
// properties: {patch: true}, // object // NOTE: not part of configurable merge
|
||||
hidden: {replace: false}, // array
|
||||
protected: {replace: false}, // array
|
||||
indexes: {patch: true}, // object
|
||||
methods: {patch: true}, // object
|
||||
mixins: {patch: true}, // object
|
||||
// validations: {patch: true}, // object // NOTE: not implemented
|
||||
relations: {patch: true}, // object
|
||||
scope: {replace: true}, // object
|
||||
scopes: {patch: true}, // object
|
||||
acls: {rank: true}, // array
|
||||
// this option controls which value assigned on child model allows deleting
|
||||
// a base model's setting
|
||||
__delete: null,
|
||||
// this option controls the default merge behaviour for settings not defined
|
||||
// in the mergePolicy specification
|
||||
__default: {replace: true},
|
||||
};
|
||||
}
|
||||
|
||||
// override mergePolicy with provided model setting if required
|
||||
if (config && typeof config === 'object' && !Array.isArray(config)) {
|
||||
// config is an object
|
||||
mergePolicy = config;
|
||||
}
|
||||
|
||||
return mergePolicy;
|
||||
};
|
||||
|
||||
/**
|
||||
* Gets properties defined with 'updateOnly' flag set to true from the model. This flag is also set to true
|
||||
* internally for the id property, if this property is generated and IdInjection is true.
|
||||
* @returns {updateOnlyProps} List of properties with updateOnly set to true.
|
||||
*/
|
||||
|
||||
ModelBaseClass.getUpdateOnlyProperties = function() {
|
||||
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);
|
||||
|
|
155
lib/observer.js
155
lib/observer.js
|
@ -1,10 +1,18 @@
|
|||
var async = require('async');
|
||||
var utils = require('./utils');
|
||||
// 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 async = require('async');
|
||||
const utils = require('./utils');
|
||||
const debug = require('debug')('loopback:observer');
|
||||
|
||||
module.exports = ObserverMixin;
|
||||
|
||||
/**
|
||||
* ObserverMixin class. Use to add observe/notifyObserversOf APIs to other
|
||||
* ObserverMixin class. Use to add observe/notifyObserversOf APIs to other
|
||||
* classes.
|
||||
*
|
||||
* @class ObserverMixin
|
||||
|
@ -14,12 +22,30 @@ function ObserverMixin() {
|
|||
|
||||
/**
|
||||
* Register an asynchronous observer for the given operation (event).
|
||||
*
|
||||
* Example:
|
||||
*
|
||||
* Registers a `before save` observer for a given model.
|
||||
*
|
||||
* ```javascript
|
||||
* MyModel.observe('before save', function filterProperties(ctx, next) {
|
||||
if (ctx.options && ctx.options.skipPropertyFilter) return next();
|
||||
if (ctx.instance) {
|
||||
FILTERED_PROPERTIES.forEach(function(p) {
|
||||
ctx.instance.unsetAttribute(p);
|
||||
});
|
||||
} else {
|
||||
FILTERED_PROPERTIES.forEach(function(p) {
|
||||
delete ctx.data[p];
|
||||
});
|
||||
}
|
||||
next();
|
||||
});
|
||||
* ```
|
||||
*
|
||||
* @param {String} operation The operation name.
|
||||
* @callback {function} listener The listener function. It will be invoked with
|
||||
* `this` set to the model constructor, e.g. `User`.
|
||||
* @param {Object} context Operation-specific context.
|
||||
* @param {function(Error=)} next The callback to call when the observer
|
||||
* has finished.
|
||||
* @end
|
||||
*/
|
||||
ObserverMixin.observe = function(operation, listener) {
|
||||
|
@ -33,6 +59,16 @@ ObserverMixin.observe = function(operation, listener) {
|
|||
|
||||
/**
|
||||
* Unregister an asynchronous observer for the given operation (event).
|
||||
*
|
||||
* Example:
|
||||
*
|
||||
* ```javascript
|
||||
* MyModel.removeObserver('before save', function removedObserver(ctx, next) {
|
||||
// some logic user want to apply to the removed observer...
|
||||
next();
|
||||
});
|
||||
* ```
|
||||
*
|
||||
* @param {String} operation The operation name.
|
||||
* @callback {function} listener The listener function.
|
||||
* @end
|
||||
|
@ -40,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);
|
||||
}
|
||||
|
@ -48,6 +84,15 @@ ObserverMixin.removeObserver = function(operation, listener) {
|
|||
|
||||
/**
|
||||
* Unregister all asynchronous observers for the given operation (event).
|
||||
*
|
||||
* Example:
|
||||
*
|
||||
* Remove all observers connected to the `before save` operation.
|
||||
*
|
||||
* ```javascript
|
||||
* MyModel.clearObservers('before save');
|
||||
* ```
|
||||
*
|
||||
* @param {String} operation The operation name.
|
||||
* @end
|
||||
*/
|
||||
|
@ -59,13 +104,32 @@ ObserverMixin.clearObservers = function(operation) {
|
|||
|
||||
/**
|
||||
* Invoke all async observers for the given operation(s).
|
||||
*
|
||||
* Example:
|
||||
*
|
||||
* Notify all async observers for the `before save` operation.
|
||||
*
|
||||
* ```javascript
|
||||
* var context = {
|
||||
Model: Model,
|
||||
instance: obj,
|
||||
isNewInstance: true,
|
||||
hookState: hookState,
|
||||
options: options,
|
||||
};
|
||||
* Model.notifyObserversOf('before save', context, function(err) {
|
||||
if (err) return cb(err);
|
||||
// user can specify the logic after the observers have been notified
|
||||
});
|
||||
* ```
|
||||
*
|
||||
* @param {String|String[]} operation The operation name(s).
|
||||
* @param {Object} context Operation-specific context.
|
||||
* @param {function(Error=)} callback The callback to call when all observers
|
||||
* has finished.
|
||||
* @callback {function(Error=)} callback The callback to call when all observers
|
||||
* have finished.
|
||||
*/
|
||||
ObserverMixin.notifyObserversOf = function(operation, context, callback) {
|
||||
var self = this;
|
||||
const self = this;
|
||||
if (!callback) callback = utils.createPromiseCallback();
|
||||
|
||||
function createNotifier(op) {
|
||||
|
@ -79,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);
|
||||
|
@ -95,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;
|
||||
|
@ -117,23 +181,45 @@ ObserverMixin._notifyBaseObservers = function(operation, context, callback) {
|
|||
};
|
||||
|
||||
/**
|
||||
* Run the given function with before/after observers. It's done in three serial
|
||||
* steps asynchronously:
|
||||
* Run the given function with before/after observers.
|
||||
*
|
||||
* It's done in three serial asynchronous steps:
|
||||
*
|
||||
* - Notify the registered observers under 'before ' + operation
|
||||
* - Execute the function
|
||||
* - Notify the registered observers under 'after ' + operation
|
||||
*
|
||||
* If an error happens, it fails fast and calls the callback with err.
|
||||
* If an error happens, it fails first and calls the callback with err.
|
||||
*
|
||||
* Example:
|
||||
*
|
||||
* ```javascript
|
||||
* var context = {
|
||||
Model: Model,
|
||||
instance: obj,
|
||||
isNewInstance: true,
|
||||
hookState: hookState,
|
||||
options: options,
|
||||
};
|
||||
* function work(done) {
|
||||
process.nextTick(function() {
|
||||
done(null, 1);
|
||||
});
|
||||
}
|
||||
* Model.notifyObserversAround('execute', context, work, function(err) {
|
||||
if (err) return cb(err);
|
||||
// user can specify the logic after the observers have been notified
|
||||
});
|
||||
* ```
|
||||
*
|
||||
* @param {String} operation The operation name
|
||||
* @param {Context} context The context object
|
||||
* @param {Function} fn The task to be invoked as fn(done) or fn(context, done)
|
||||
* @param {Function} callback The callback function
|
||||
* @callback {Function} callback The callback function
|
||||
* @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
|
||||
|
@ -146,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
475
lib/relations.js
475
lib/relations.js
|
@ -1,13 +1,20 @@
|
|||
// 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;
|
||||
|
||||
/**
|
||||
* RelationMixin class. Use to define relationships between models.
|
||||
* RelationMixin class. Use to define relationships between models.
|
||||
*
|
||||
* @class RelationMixin
|
||||
*/
|
||||
|
@ -15,13 +22,13 @@ function RelationMixin() {
|
|||
}
|
||||
|
||||
/**
|
||||
* Define a "one to many" relationship by specifying the model name
|
||||
*
|
||||
* Define a "one to many" relationship by specifying the model name.
|
||||
*
|
||||
* Examples:
|
||||
* ```
|
||||
* User.hasMany(Post, {as: 'posts', foreignKey: 'authorId'});
|
||||
* ```
|
||||
*
|
||||
*
|
||||
* ```
|
||||
* Book.hasMany(Chapter);
|
||||
* ```
|
||||
|
@ -34,35 +41,43 @@ function RelationMixin() {
|
|||
*
|
||||
* ```js
|
||||
* Book.create(function(err, book) {
|
||||
*
|
||||
*
|
||||
* // Create a chapter instance ready to be saved in the data source.
|
||||
* var chapter = book.chapters.build({name: 'Chapter 1'});
|
||||
*
|
||||
*
|
||||
* // Save the new chapter
|
||||
* chapter.save();
|
||||
*
|
||||
*
|
||||
* // you can also call the Chapter.create method with the `chapters` property which will build a chapter
|
||||
* // instance and save the it in the data source.
|
||||
* book.chapters.create({name: 'Chapter 2'}, function(err, savedChapter) {
|
||||
* // this callback is optional
|
||||
* // this callback is optional
|
||||
* });
|
||||
*
|
||||
* // Query chapters for the book
|
||||
* book.chapters(function(err, chapters) { // all chapters with bookId = book.id
|
||||
*
|
||||
* // Query chapters for the book
|
||||
* book.chapters(function(err, chapters) {
|
||||
* // all chapters with bookId = book.id
|
||||
* console.log(chapters);
|
||||
* });
|
||||
*
|
||||
*
|
||||
* // Query chapters for the book with a filter
|
||||
* book.chapters({where: {name: 'test'}, function(err, chapters) {
|
||||
* // All chapters with bookId = book.id and name = 'test'
|
||||
* console.log(chapters);
|
||||
* });
|
||||
* });
|
||||
*```
|
||||
* ```
|
||||
*
|
||||
* @param {Object|String} modelTo Model object (or String name of model) to which you are creating the relationship.
|
||||
* @options {Object} parameters Configuration parameters; see below.
|
||||
* @options {Object} params Configuration parameters; see below.
|
||||
* @property {String} as Name of the property in the referring model that corresponds to the foreign key field in the related model.
|
||||
* @property {String} foreignKey Property name of foreign key field.
|
||||
* @property {Object} model Model object
|
||||
* @property {String} polymorphic Define a polymorphic relation name.
|
||||
* @property {String} through Name of the through model.
|
||||
* @property {String} keyThrough Property name of the foreign key in the through model.
|
||||
* @property {Object|Function} scope Explicitly define additional scopes.
|
||||
* @property {Boolean} invert Specify if the relation is inverted.
|
||||
* @property {Object} model The model object.
|
||||
*/
|
||||
RelationMixin.hasMany = function hasMany(modelTo, params) {
|
||||
return RelationDefinition.hasMany(this, modelTo, params);
|
||||
|
@ -86,12 +101,13 @@ RelationMixin.hasMany = function hasMany(modelTo, params) {
|
|||
* Get the User object for the post author synchronously:
|
||||
* ```
|
||||
* post.author();
|
||||
* ```
|
||||
* Set the author to be the given user:
|
||||
* ```
|
||||
* post.author(user)
|
||||
* post.author(user)
|
||||
* ```
|
||||
* Examples:
|
||||
*
|
||||
*
|
||||
* Suppose the model Post has a *belongsTo* relationship with User (the author of the post). You could declare it this way:
|
||||
* ```js
|
||||
* Post.belongsTo(User, {as: 'author', foreignKey: 'userId'});
|
||||
|
@ -116,15 +132,20 @@ RelationMixin.hasMany = function hasMany(modelTo, params) {
|
|||
* @param {Class|String} modelTo Model object (or String name of model) to which you are creating the relationship.
|
||||
* @options {Object} params Configuration parameters; see below.
|
||||
* @property {String} as Name of the property in the referring model that corresponds to the foreign key field in the related model.
|
||||
* @property {String} primaryKey Property name of primary key field.
|
||||
* @property {String} foreignKey Name of foreign key property.
|
||||
*
|
||||
* @property {Object|Function} scope Explicitly define additional scopes.
|
||||
* @property {Object} properties Properties inherited from the parent object.
|
||||
* @property {Object} options Property level options.
|
||||
* @property {Boolean} options.invertProperties Specify if the properties should be inverted.
|
||||
*/
|
||||
RelationMixin.belongsTo = function (modelTo, params) {
|
||||
RelationMixin.belongsTo = function(modelTo, params) {
|
||||
return RelationDefinition.belongsTo(this, modelTo, params);
|
||||
};
|
||||
|
||||
/**
|
||||
* A hasAndBelongsToMany relation creates a direct many-to-many connection with another model, with no intervening model.
|
||||
*
|
||||
* For example, if your application includes users and groups, with each group having many users and each user appearing
|
||||
* in many groups, you could declare the models this way:
|
||||
* ```
|
||||
|
@ -144,32 +165,436 @@ RelationMixin.belongsTo = function (modelTo, params) {
|
|||
* ```
|
||||
* Remove the user from the group:
|
||||
* ```
|
||||
* user.groups.remove(group, callback);
|
||||
* user.groups.remove(group, callback);
|
||||
* ```
|
||||
*
|
||||
*
|
||||
* @param {String|Object} modelTo Model object (or String name of model) to which you are creating the relationship.
|
||||
* the relation
|
||||
* @options {Object} params Configuration parameters; see below.
|
||||
* @property {String} as Name of the property in the referring model that corresponds to the foreign key field in the related model.
|
||||
* @property {String} foreignKey Property name of foreign key field.
|
||||
* @property {Object} model Model object
|
||||
* @property {String} throughTable The table name of the through model.
|
||||
* @property {String} through Name of the through model.
|
||||
* @property {String} polymorphic Define a polymorphic relation name.
|
||||
* @property {Object|Function} scope Explicitly define additional scopes.
|
||||
* @property {Object} model The model object.
|
||||
*/
|
||||
RelationMixin.hasAndBelongsToMany = function hasAndBelongsToMany(modelTo, params) {
|
||||
return RelationDefinition.hasAndBelongsToMany(this, modelTo, params);
|
||||
};
|
||||
|
||||
/**
|
||||
* Define a "one to one" relationship by specifying the model name.
|
||||
*
|
||||
* Examples:
|
||||
* ```
|
||||
* Supplier.hasOne(Account, {as: 'account', foreignKey: 'supplierId'});
|
||||
* ```
|
||||
*
|
||||
* If the target model doesn’t have a foreign key property, LoopBack will add a property with the same name.
|
||||
*
|
||||
* The type of the property will be the same as the type of the target model’s id property.
|
||||
*
|
||||
* Please note the foreign key property is defined on the target model (in this example, Account).
|
||||
*
|
||||
* If you don’t specify them, then LoopBack derives the relation name and foreign key as follows:
|
||||
* - Relation name: Camel case of the model name, for example, for the “supplier” model the relation is “supplier”.
|
||||
* - Foreign key: The relation name appended with Id, for example, for relation name “supplier” the default foreign key is “supplierId”.
|
||||
*
|
||||
* Build a new account for the supplier with the supplierId to be set to the id of the supplier.
|
||||
* ```js
|
||||
* var supplier = supplier.account.build(data);
|
||||
* ```
|
||||
*
|
||||
* Create a new account for the supplier. If there is already an account, an error will be reported.
|
||||
* ```js
|
||||
* supplier.account.create(data, function(err, account) {
|
||||
* ...
|
||||
* });
|
||||
* ```
|
||||
*
|
||||
* Find the supplier's account model.
|
||||
* ```js
|
||||
* supplier.account(function(err, account) {
|
||||
* ...
|
||||
* });
|
||||
* ```
|
||||
*
|
||||
* Update the associated account.
|
||||
* ```js
|
||||
* supplier.account.update({balance: 100}, function(err, account) {
|
||||
* ...
|
||||
* });
|
||||
* ```
|
||||
*
|
||||
* Remove the account for the supplier.
|
||||
* ```js
|
||||
* supplier.account.destroy(function(err) {
|
||||
* ...
|
||||
* });
|
||||
* ```
|
||||
*
|
||||
* @param {Object|String} modelTo Model object (or String name of model) to which you are creating the relationship.
|
||||
* @options {Object} params Configuration parameters; see below.
|
||||
* @property {String} as Name of the property in the referring model that corresponds to the foreign key field in the related model.
|
||||
* @property {String} primaryKey Property name of primary key field.
|
||||
* @property {String} foreignKey Property name of foreign key field.
|
||||
* @property {String} polymorphic Define a polymorphic relation name.
|
||||
* @property {Object|Function} scope Explicitly define additional scopes.
|
||||
* @property {Object} model The model object.
|
||||
* @property {Object} properties Properties inherited from the parent object.
|
||||
* @property {Function} methods Scoped methods for the given relation.
|
||||
*/
|
||||
RelationMixin.hasOne = function hasOne(modelTo, params) {
|
||||
return RelationDefinition.hasOne(this, modelTo, params);
|
||||
};
|
||||
|
||||
/**
|
||||
* References one or more instances of the target model.
|
||||
*
|
||||
* For example, a Customer model references one or more instances of the Account model.
|
||||
*
|
||||
* Define the relation in the model definition:
|
||||
*
|
||||
* - Definition of Customer model:
|
||||
* ```json
|
||||
* {
|
||||
"name": "Customer",
|
||||
"base": "PersistedModel",
|
||||
"idInjection": true,
|
||||
"properties": {
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
"age": {
|
||||
"type": "number"
|
||||
}
|
||||
},
|
||||
"validations": [],
|
||||
"relations": {
|
||||
"accounts": {
|
||||
"type": "referencesMany",
|
||||
"model": "Account",
|
||||
"foreignKey": "accountIds",
|
||||
"options": {
|
||||
"validate": true,
|
||||
"forceId": false
|
||||
}
|
||||
}
|
||||
},
|
||||
"acls": [],
|
||||
"methods": {}
|
||||
}
|
||||
* ```
|
||||
*
|
||||
* - Definition of Account model:
|
||||
* ```json
|
||||
* {
|
||||
"name": "Account",
|
||||
"base": "PersistedModel",
|
||||
"idInjection": true,
|
||||
"properties": {
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
"balance": {
|
||||
"type": "number"
|
||||
}
|
||||
},
|
||||
"validations": [],
|
||||
"relations": {},
|
||||
"acls": [],
|
||||
"methods": {}
|
||||
}
|
||||
* ```
|
||||
*
|
||||
* On the bootscript, create a customer instance and for that customer instance reference many account instances.
|
||||
*
|
||||
* For example:
|
||||
* ```javascript
|
||||
* var Customer = app.models.Customer;
|
||||
var accounts = [
|
||||
{
|
||||
name: 'Checking',
|
||||
balance: 5000
|
||||
},
|
||||
{
|
||||
name: 'Saving',
|
||||
balance: 2000
|
||||
}
|
||||
];
|
||||
Customer.create({name: 'Mary Smith'}, function(err, customer) {
|
||||
console.log('Customer:', customer);
|
||||
async.each(accounts, function(account, done) {
|
||||
customer.accounts.create(account, done);
|
||||
}, function(err) {
|
||||
console.log('Customer with accounts:', customer);
|
||||
customer.accounts(console.log);
|
||||
cb(err);
|
||||
});
|
||||
});
|
||||
* ```
|
||||
*
|
||||
* Sample referencesMany model data:
|
||||
* ```javascript
|
||||
* {
|
||||
id: 1,
|
||||
name: 'John Smith',
|
||||
accounts: [
|
||||
"saving-01", "checking-01",
|
||||
]
|
||||
}
|
||||
* ```
|
||||
*
|
||||
* Supported helper methods:
|
||||
* - customer.accounts()
|
||||
* - customer.accounts.create()
|
||||
* - customer.accounts.build()
|
||||
* - customer.accounts.findById()
|
||||
* - customer.accounts.destroy()
|
||||
* - customer.accounts.updateById()
|
||||
* - customer.accounts.exists()
|
||||
* - customer.accounts.add()
|
||||
* - customer.accounts.remove()
|
||||
* - customer.accounts.at()
|
||||
*
|
||||
* @param {Object|String} modelTo Model object (or String name of model) to which you are creating the relationship.
|
||||
* @options {Object} params Configuration parameters; see below.
|
||||
* @property {String} as Name of the property in the referring model that corresponds to the foreign key field in the related model.
|
||||
* @property {Any} default The default value.
|
||||
* @property {Object} options Options to specify for the relationship.
|
||||
* @property {Boolean} options.forceId Force generation of id for embedded items. Default is false.
|
||||
* @property {Boolean} options.validate Denote if the embedded items should be validated. Default is true.
|
||||
* @property {Boolean} options.persistent Denote if the embedded items should be persisted. Default is false.
|
||||
* @property {Object|Function} scope Explicitly define additional scopes.
|
||||
* @property {String} foreignKey Property name of foreign key field.
|
||||
* @property {Object} properties Properties inherited from the parent object.
|
||||
* @property {Function} methods Scoped methods for the given relation.
|
||||
*/
|
||||
RelationMixin.referencesMany = function referencesMany(modelTo, params) {
|
||||
return RelationDefinition.referencesMany(this, modelTo, params);
|
||||
};
|
||||
|
||||
/**
|
||||
* Represent a model that embeds another model.
|
||||
*
|
||||
* For example, a Customer embeds one billingAddress from the Address model.
|
||||
*
|
||||
* - Define the relation in bootscript:
|
||||
* ```js
|
||||
* Customer.embedsOne(Address, {
|
||||
* as: 'address', // default to the relation name - address
|
||||
* property: 'billingAddress' // default to addressItem
|
||||
* });
|
||||
* ```
|
||||
*
|
||||
* OR, define the relation in the model definition:
|
||||
*
|
||||
* - Definition of Customer model:
|
||||
* ```json
|
||||
* {
|
||||
"name": "Customer",
|
||||
"base": "PersistedModel",
|
||||
"idInjection": true,
|
||||
"properties": {
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
"age": {
|
||||
"type": "number"
|
||||
}
|
||||
},
|
||||
"validations": [],
|
||||
"relations": {
|
||||
"address": {
|
||||
"type": "embedsOne",
|
||||
"model": "Address",
|
||||
"property": "billingAddress",
|
||||
"options": {
|
||||
"validate": true,
|
||||
"forceId": false
|
||||
}
|
||||
}
|
||||
},
|
||||
"acls": [],
|
||||
"methods": {}
|
||||
}
|
||||
* ```
|
||||
*
|
||||
* - Definition of Address model:
|
||||
* ```json
|
||||
* {
|
||||
"name": "Address",
|
||||
"base": "Model",
|
||||
"idInjection": true,
|
||||
"properties": {
|
||||
"street": {
|
||||
"type": "string"
|
||||
},
|
||||
"city": {
|
||||
"type": "string"
|
||||
},
|
||||
"state": {
|
||||
"type": "string"
|
||||
},
|
||||
"zipCode": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"validations": [],
|
||||
"relations": {},
|
||||
"acls": [],
|
||||
"methods": {}
|
||||
}
|
||||
* ```
|
||||
*
|
||||
* Sample embedded model data:
|
||||
* ```javascript
|
||||
* {
|
||||
id: 1,
|
||||
name: 'John Smith',
|
||||
billingAddress: {
|
||||
street: '123 Main St',
|
||||
city: 'San Jose',
|
||||
state: 'CA',
|
||||
zipCode: '95124'
|
||||
}
|
||||
}
|
||||
* ```
|
||||
*
|
||||
* Supported helper methods:
|
||||
* - customer.address()
|
||||
* - customer.address.build()
|
||||
* - customer.address.create()
|
||||
* - customer.address.update()
|
||||
* - customer.address.destroy()
|
||||
* - customer.address.value()
|
||||
*
|
||||
* @param {Object|String} modelTo Model object (or String name of model) to which you are creating the relationship.
|
||||
* @options {Object} params Configuration parameters; see below.
|
||||
* @property {String} as Name of the property in the referring model that corresponds to the foreign key field in the related model.
|
||||
* @property {String} property Name of the property for the embedded item.
|
||||
* @property {Any} default The default value.
|
||||
* @property {Object} options Options to specify for the relationship.
|
||||
* @property {Boolean} options.forceId Force generation of id for embedded items. Default is false.
|
||||
* @property {Boolean} options.validate Denote if the embedded items should be validated. Default is true.
|
||||
* @property {Boolean} options.persistent Denote if the embedded items should be persisted. Default is false.
|
||||
* @property {Object|Function} scope Explicitly define additional scopes.
|
||||
* @property {Object} properties Properties inherited from the parent object.
|
||||
* @property {Function} methods Scoped methods for the given relation.
|
||||
*/
|
||||
RelationMixin.embedsOne = function embedsOne(modelTo, params) {
|
||||
return RelationDefinition.embedsOne(this, modelTo, params);
|
||||
};
|
||||
|
||||
/**
|
||||
* Represent a model that can embed many instances of another model.
|
||||
*
|
||||
* For example, a Customer can have multiple email addresses and each email address is a complex object that contains label and address.
|
||||
*
|
||||
* Define the relation code in bootscript:
|
||||
* ```javascript
|
||||
Customer.embedsMany(EmailAddress, {
|
||||
as: 'emails', // default to the relation name - emailAddresses
|
||||
property: 'emailList' // default to emailAddressItems
|
||||
});
|
||||
* ```
|
||||
*
|
||||
* OR, define the relation in the model definition:
|
||||
*
|
||||
* - Definition of Customer model:
|
||||
* ```json
|
||||
* {
|
||||
"name": "Customer",
|
||||
"base": "PersistedModel",
|
||||
"idInjection": true,
|
||||
"properties": {
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
"age": {
|
||||
"type": "number"
|
||||
}
|
||||
},
|
||||
"validations": [],
|
||||
"relations": {
|
||||
"emails": {
|
||||
"type": "embedsMany",
|
||||
"model": "EmailAddress",
|
||||
"property": "emailList",
|
||||
"options": {
|
||||
"validate": true,
|
||||
"forceId": false
|
||||
}
|
||||
}
|
||||
},
|
||||
"acls": [],
|
||||
"methods": {}
|
||||
}
|
||||
* ```
|
||||
*
|
||||
* - Definition of EmailAddress model:
|
||||
* ```json
|
||||
* {
|
||||
"name": "EmailAddress",
|
||||
"base": "Model",
|
||||
"idInjection": true,
|
||||
"properties": {
|
||||
"label": {
|
||||
"type": "string"
|
||||
},
|
||||
"address": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"validations": [],
|
||||
"relations": {},
|
||||
"acls": [],
|
||||
"methods": {}
|
||||
}
|
||||
* ```
|
||||
*
|
||||
* Sample embedded model data:
|
||||
* ```javascript
|
||||
* {
|
||||
id: 1,
|
||||
name: 'John Smith',
|
||||
emails: [{
|
||||
label: 'work',
|
||||
address: 'john@xyz.com'
|
||||
}, {
|
||||
label: 'home',
|
||||
address: 'john@gmail.com'
|
||||
}]
|
||||
}
|
||||
* ```
|
||||
*
|
||||
* Supported helper methods:
|
||||
* - customer.emails()
|
||||
* - customer.emails.create()
|
||||
* - customer.emails.build()
|
||||
* - customer.emails.findById()
|
||||
* - customer.emails.destroyById()
|
||||
* - customer.emails.updateById()
|
||||
* - customer.emails.exists()
|
||||
* - customer.emails.add()
|
||||
* - customer.emails.remove()
|
||||
* - customer.emails.at()
|
||||
* - customer.emails.value()
|
||||
*
|
||||
* @param {Object|String} modelTo Model object (or String name of model) to which you are creating the relationship.
|
||||
* @options {Object} params Configuration parameters; see below.
|
||||
* @property {String} as Name of the property in the referring model that corresponds to the foreign key field in the related model.
|
||||
* @property {String} property Name of the property for the embedded item.
|
||||
* @property {Any} default The default value.
|
||||
* @property {Object} options Options to specify for the relationship.
|
||||
* @property {Boolean} options.forceId Force generation of id for embedded items. Default is false.
|
||||
* @property {Boolean} options.validate Denote if the embedded items should be validated. Default is true.
|
||||
* @property {Boolean} options.persistent Denote if the embedded items should be persisted. Default is false.
|
||||
* @property {String} polymorphic Define a polymorphic relation name.
|
||||
* @property {Object|Function} scope Explicitly define additional scopes.
|
||||
* @property {Object} properties Properties inherited from the parent object.
|
||||
* @property {Function} methods Scoped methods for the given relation.
|
||||
*/
|
||||
RelationMixin.embedsMany = function embedsMany(modelTo, params) {
|
||||
return RelationDefinition.embedsMany(this, modelTo, params);
|
||||
};
|
||||
|
|
258
lib/scope.js
258
lib/scope.js
|
@ -1,9 +1,21 @@
|
|||
var i8n = require('inflection');
|
||||
var utils = require('./utils');
|
||||
var defineCachedRelations = utils.defineCachedRelations;
|
||||
var setScopeValuesFromWhere = utils.setScopeValuesFromWhere;
|
||||
var mergeQuery = utils.mergeQuery;
|
||||
var DefaultModelBaseClass = require('./model.js');
|
||||
// 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';
|
||||
|
||||
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
|
||||
|
@ -21,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 + '`.';
|
||||
|
@ -47,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)
|
||||
|
@ -74,25 +86,86 @@ ScopeDefinition.prototype.related = function(receiver, scopeParams, condOrRefres
|
|||
}
|
||||
}
|
||||
cb = cb || utils.createPromiseCallback();
|
||||
|
||||
if (!self.__cachedRelations || self.__cachedRelations[name] === undefined
|
||||
|| actualRefresh) {
|
||||
const refreshIsNeeded = !self.__cachedRelations ||
|
||||
self.__cachedRelations[name] === undefined ||
|
||||
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);
|
||||
targetModel.find(params, options, function (err, data) {
|
||||
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
|
||||
const scopeOnRelatedModel = params.collect &&
|
||||
params.include.scope !== null &&
|
||||
typeof params.include.scope === 'object';
|
||||
let filter, queryRelated;
|
||||
if (scopeOnRelatedModel) {
|
||||
filter = params.include;
|
||||
// The filter applied on relatedModel
|
||||
queryRelated = filter.scope;
|
||||
delete params.include.scope;
|
||||
}
|
||||
|
||||
targetModel.find(params, options, function(err, data) {
|
||||
if (!err && saveOnCache) {
|
||||
defineCachedRelations(self);
|
||||
self.__cachedRelations[name] = data;
|
||||
}
|
||||
cb(err, data);
|
||||
|
||||
if (scopeOnRelatedModel === true) {
|
||||
const relatedModel = targetModel.relations[filter.relation].modelTo;
|
||||
const IdKey = idName(relatedModel);
|
||||
|
||||
// return {inq: [1,2,3]}}
|
||||
const smartMerge = function(idCollection, qWhere) {
|
||||
if (!qWhere[IdKey]) return idCollection;
|
||||
let merged = {};
|
||||
|
||||
const idsA = idCollection.inq;
|
||||
const idsB = qWhere[IdKey].inq ? qWhere[IdKey].inq : [qWhere[IdKey]];
|
||||
|
||||
const intersect = _.intersectionWith(idsA, idsB, _.isEqual);
|
||||
if (intersect.length === 1) merged = intersect[0];
|
||||
if (intersect.length > 1) merged = {inq: intersect};
|
||||
|
||||
return merged;
|
||||
};
|
||||
|
||||
if (queryRelated.where !== undefined) {
|
||||
// Merge queryRelated filter and targetId filter
|
||||
const IdKeyCondition = {};
|
||||
IdKeyCondition[IdKey] = smartMerge(collectTargetIds(data, IdKey),
|
||||
queryRelated.where);
|
||||
|
||||
// if the id in filter doesn't exist after the merge,
|
||||
// return empty result
|
||||
if (_.isObject(IdKeyCondition[IdKey]) && _.isEmpty(IdKeyCondition[IdKey])) return cb(null, []);
|
||||
|
||||
const mergedWhere = {
|
||||
and: [
|
||||
IdKeyCondition,
|
||||
_.omit(queryRelated.where, IdKey),
|
||||
],
|
||||
};
|
||||
queryRelated.where = mergedWhere;
|
||||
} else {
|
||||
queryRelated.where = {};
|
||||
queryRelated.where[IdKey] = collectTargetIds(data, IdKey);
|
||||
}
|
||||
|
||||
relatedModel.find(queryRelated, options, cb);
|
||||
} else {
|
||||
cb(err, data);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
// Return from cache
|
||||
cb(null, self.__cachedRelations[name]);
|
||||
}
|
||||
return cb.promise;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Define a scope method
|
||||
|
@ -101,7 +174,7 @@ ScopeDefinition.prototype.related = function(receiver, scopeParams, condOrRefres
|
|||
*/
|
||||
ScopeDefinition.prototype.defineMethod = function(name, fn) {
|
||||
return this.methods[name] = fn;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Define a scope to the class
|
||||
|
@ -130,18 +203,18 @@ 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,
|
||||
name: name,
|
||||
params: params,
|
||||
methods: methods,
|
||||
options: options
|
||||
options: options,
|
||||
});
|
||||
|
||||
if(isStatic) {
|
||||
if (isStatic) {
|
||||
cls.scopes = cls.scopes || {};
|
||||
cls.scopes[name] = definition;
|
||||
} else {
|
||||
|
@ -164,11 +237,11 @@ function defineScope(cls, targetClass, name, params, methods, options) {
|
|||
* user.accounts.create(act, cb).
|
||||
*
|
||||
*/
|
||||
get: function () {
|
||||
var targetModel = definition.targetModel(this);
|
||||
var self = this;
|
||||
get: function() {
|
||||
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);
|
||||
|
@ -176,8 +249,10 @@ function defineScope(cls, targetClass, name, params, methods, options) {
|
|||
return self.__cachedRelations[name];
|
||||
}
|
||||
} else {
|
||||
if (typeof condOrRefresh === 'function'
|
||||
&& options === undefined && cb === undefined) {
|
||||
const condOrRefreshIsCallBack = typeof condOrRefresh === 'function' &&
|
||||
options === undefined &&
|
||||
cb === undefined;
|
||||
if (condOrRefreshIsCallBack) {
|
||||
// customer.orders(cb)
|
||||
cb = condOrRefresh;
|
||||
options = {};
|
||||
|
@ -187,23 +262,14 @@ function defineScope(cls, targetClass, name, params, methods, options) {
|
|||
cb = options;
|
||||
options = {};
|
||||
}
|
||||
options = options || {}
|
||||
options = options || {};
|
||||
// Check if there is a through model
|
||||
// see https://github.com/strongloop/loopback/issues/1076
|
||||
if (f._scope.collect &&
|
||||
condOrRefresh !== null && typeof condOrRefresh === 'object') {
|
||||
//extract the paging filters to the through model
|
||||
['limit','offset','skip','order'].forEach(function(pagerFilter){
|
||||
if(typeof(condOrRefresh[pagerFilter]) !== 'undefined'){
|
||||
f._scope[pagerFilter] = condOrRefresh[pagerFilter];
|
||||
delete condOrRefresh[pagerFilter];
|
||||
}
|
||||
});
|
||||
// Adjust the include so that the condition will be applied to
|
||||
// the target model
|
||||
f._scope.include = {
|
||||
relation: f._scope.collect,
|
||||
scope: condOrRefresh
|
||||
scope: condOrRefresh,
|
||||
};
|
||||
condOrRefresh = {};
|
||||
}
|
||||
|
@ -217,24 +283,28 @@ function defineScope(cls, targetClass, name, params, methods, options) {
|
|||
|
||||
f._targetClass = targetModel.modelName;
|
||||
if (f._scope.collect) {
|
||||
f._targetClass = i8n.camelize(f._scope.collect);
|
||||
const rel = targetModel.relations[f._scope.collect];
|
||||
f._targetClass = rel && rel.modelTo && rel.modelTo.modelName || i8n.camelize(f._scope.collect);
|
||||
}
|
||||
|
||||
f.getAsync = function(condOrRefresh, options, cb) {
|
||||
if (typeof condOrRefresh === 'function'
|
||||
&& options === undefined && cb === undefined) {
|
||||
// customer.orders.getAsync(cb)
|
||||
f.find = function(condOrRefresh, options, cb) {
|
||||
if (typeof condOrRefresh === 'function' &&
|
||||
options === undefined && cb === undefined) {
|
||||
cb = condOrRefresh;
|
||||
options = {};
|
||||
condOrRefresh = {};
|
||||
} else if (typeof options === 'function' && cb === undefined) {
|
||||
// customer.orders.getAsync(condOrRefresh, cb);
|
||||
cb = options;
|
||||
options = {};
|
||||
}
|
||||
options = options || {}
|
||||
options = options || {};
|
||||
return definition.related(self, f._scope, condOrRefresh, options, cb);
|
||||
}
|
||||
};
|
||||
|
||||
f.getAsync = function() {
|
||||
deprecated(g.f('Scope method "getAsync()" is deprecated, use "find()" instead.'));
|
||||
return this.find.apply(this, arguments);
|
||||
};
|
||||
|
||||
f.build = build;
|
||||
f.create = create;
|
||||
|
@ -244,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);
|
||||
}
|
||||
|
||||
|
@ -254,77 +324,77 @@ function defineScope(cls, targetClass, name, params, methods, options) {
|
|||
// Station.scope('active', {where: {isActive: true}});
|
||||
// Station.scope('subway', {where: {isUndeground: true}});
|
||||
// Station.active.subway(cb);
|
||||
Object.keys(targetClass._scopeMeta).forEach(function (name) {
|
||||
Object.keys(targetClass._scopeMeta).forEach(function(name) {
|
||||
Object.defineProperty(f, name, {
|
||||
enumerable: false,
|
||||
get: function () {
|
||||
get: function() {
|
||||
mergeQuery(f._scope, targetModel._scopeMeta[name]);
|
||||
return f;
|
||||
}
|
||||
},
|
||||
});
|
||||
}.bind(self));
|
||||
return f;
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
// 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 fn_create = function () {
|
||||
var f = this[name].create;
|
||||
const fnCreate = function() {
|
||||
const f = this[name].create;
|
||||
f.apply(this[name], arguments);
|
||||
};
|
||||
|
||||
cls['__create__' + name] = fn_create;
|
||||
cls['__create__' + name] = fnCreate;
|
||||
|
||||
var fn_delete = function () {
|
||||
var f = this[name].destroyAll;
|
||||
const fnDelete = function() {
|
||||
const f = this[name].destroyAll;
|
||||
f.apply(this[name], arguments);
|
||||
};
|
||||
|
||||
cls['__delete__' + name] = fn_delete;
|
||||
cls['__delete__' + name] = fnDelete;
|
||||
|
||||
var fn_update = function () {
|
||||
var f = this[name].updateAll;
|
||||
const fnUpdate = function() {
|
||||
const f = this[name].updateAll;
|
||||
f.apply(this[name], arguments);
|
||||
};
|
||||
|
||||
cls['__update__' + name] = fn_update;
|
||||
cls['__update__' + name] = fnUpdate;
|
||||
|
||||
var fn_findById = function (cb) {
|
||||
var f = this[name].findById;
|
||||
const fnFindById = function(cb) {
|
||||
const f = this[name].findById;
|
||||
f.apply(this[name], arguments);
|
||||
};
|
||||
|
||||
cls['__findById__' + name] = fn_findById;
|
||||
cls['__findById__' + name] = fnFindById;
|
||||
|
||||
var fn_findOne = function (cb) {
|
||||
var f = this[name].findOne;
|
||||
const fnFindOne = function(cb) {
|
||||
const f = this[name].findOne;
|
||||
f.apply(this[name], arguments);
|
||||
};
|
||||
|
||||
cls['__findOne__' + name] = fn_findOne;
|
||||
cls['__findOne__' + name] = fnFindOne;
|
||||
|
||||
var fn_count = function (cb) {
|
||||
var f = this[name].count;
|
||||
const fnCount = function(cb) {
|
||||
const f = this[name].count;
|
||||
f.apply(this[name], arguments);
|
||||
};
|
||||
|
||||
cls['__count__' + name] = fn_count;
|
||||
cls['__count__' + name] = fnCount;
|
||||
|
||||
// and it should have create/build methods with binded thisModelNameId param
|
||||
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);
|
||||
}
|
||||
|
@ -362,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);
|
||||
}
|
||||
|
||||
|
@ -382,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);
|
||||
}
|
||||
|
||||
|
@ -407,12 +477,12 @@ 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);
|
||||
|
@ -430,9 +500,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) || {};
|
||||
filter = mergeQuery({ where: scoped }, filter || {});
|
||||
const targetModel = definition.targetModel(this._receiver);
|
||||
const scoped = (this._scope && this._scope.where) || {};
|
||||
filter = mergeQuery({where: scoped}, filter || {});
|
||||
return targetModel.findOne(filter, options, cb);
|
||||
}
|
||||
|
||||
|
@ -448,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,10 +1,18 @@
|
|||
var debug = require('debug')('loopback:connector:transaction');
|
||||
var uuid = require('node-uuid');
|
||||
var utils = require('./utils');
|
||||
var jutil = require('./jutil');
|
||||
var ObserverMixin = require('./observer');
|
||||
// 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
|
||||
|
||||
var Transaction = require('loopback-connector').Transaction;
|
||||
'use strict';
|
||||
|
||||
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');
|
||||
|
||||
const Transaction = require('loopback-connector').Transaction;
|
||||
|
||||
module.exports = TransactionMixin;
|
||||
|
||||
|
@ -17,35 +25,9 @@ function TransactionMixin() {
|
|||
}
|
||||
|
||||
/**
|
||||
* Begin a new transaction
|
||||
* @param {Object|String} [options] Options can be one of the forms:
|
||||
* - Object: {isolationLevel: '...', timeout: 1000}
|
||||
* - String: isolationLevel
|
||||
* Begin a new transaction.
|
||||
*
|
||||
* Valid values of `isolationLevel` are:
|
||||
*
|
||||
* - Transaction.READ_COMMITTED = 'READ COMMITTED'; // default
|
||||
* - Transaction.READ_UNCOMMITTED = 'READ UNCOMMITTED';
|
||||
* - Transaction.SERIALIZABLE = 'SERIALIZABLE';
|
||||
* - Transaction.REPEATABLE_READ = 'REPEATABLE READ';
|
||||
*
|
||||
* @param {Function} cb Callback function. It calls back with (err, transaction).
|
||||
* To pass the transaction context to one of the CRUD methods, use the `options`
|
||||
* argument with `transaction` property, for example,
|
||||
*
|
||||
* ```js
|
||||
*
|
||||
* MyModel.beginTransaction('READ COMMITTED', function(err, tx) {
|
||||
* MyModel.create({x: 1, y: 'a'}, {transaction: tx}, function(err, inst) {
|
||||
* MyModel.find({x: 1}, {transaction: tx}, function(err, results) {
|
||||
* // ...
|
||||
* tx.commit(function(err) {...});
|
||||
* });
|
||||
* });
|
||||
* });
|
||||
* ```
|
||||
*
|
||||
* The transaction can be committed or rolled back. If timeout happens, the
|
||||
* A transaction can be committed or rolled back. If timeout happens, the
|
||||
* transaction will be rolled back. Please note a transaction is typically
|
||||
* associated with a pooled connection. Committing or rolling back a transaction
|
||||
* will release the connection back to the pool.
|
||||
|
@ -58,22 +40,57 @@ function TransactionMixin() {
|
|||
* source/connector instance. CRUD methods will not join the current transaction
|
||||
* if its model is not attached the same data source.
|
||||
*
|
||||
* Example:
|
||||
*
|
||||
* To pass the transaction context to one of the CRUD methods, use the `options`
|
||||
* argument with `transaction` property, for example,
|
||||
*
|
||||
* ```js
|
||||
* MyModel.beginTransaction('READ COMMITTED', function(err, tx) {
|
||||
* MyModel.create({x: 1, y: 'a'}, {transaction: tx}, function(err, inst) {
|
||||
* MyModel.find({x: 1}, {transaction: tx}, function(err, results) {
|
||||
* // ...
|
||||
* tx.commit(function(err) {...});
|
||||
* });
|
||||
* });
|
||||
* });
|
||||
* ```
|
||||
*
|
||||
* @param {Object|String} options Options to be passed upon transaction.
|
||||
*
|
||||
* Can be one of the forms:
|
||||
* - Object: {isolationLevel: '...', timeout: 1000}
|
||||
* - String: isolationLevel
|
||||
*
|
||||
* Valid values of `isolationLevel` are:
|
||||
*
|
||||
* - Transaction.READ_COMMITTED = 'READ COMMITTED'; // default
|
||||
* - Transaction.READ_UNCOMMITTED = 'READ UNCOMMITTED';
|
||||
* - Transaction.SERIALIZABLE = 'SERIALIZABLE';
|
||||
* - Transaction.REPEATABLE_READ = 'REPEATABLE READ';
|
||||
* @callback {Function} cb Callback function.
|
||||
* @returns {Promise|undefined} Returns a callback promise.
|
||||
*/
|
||||
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);
|
||||
if (transaction) {
|
||||
// NOTE(lehni) As part of the process of moving the handling of
|
||||
// transaction id and timeout from TransactionMixin.beginTransaction() to
|
||||
// Transaction.begin() in loopback-connector, switch to only setting id
|
||||
// and timeout if it wasn't taken care of already by Transaction.begin().
|
||||
// Eventually, we can remove the following two if-blocks altogether.
|
||||
if (!transaction.id) {
|
||||
// Set an informational transaction id
|
||||
transaction.id = uuid.v1();
|
||||
}
|
||||
if (options.timeout) {
|
||||
setTimeout(function() {
|
||||
var context = {
|
||||
if (options.timeout && !transaction.timeout) {
|
||||
transaction.timeout = setTimeout(function() {
|
||||
const context = {
|
||||
transaction: transaction,
|
||||
operation: 'timeout'
|
||||
operation: 'timeout',
|
||||
};
|
||||
transaction.notifyObserversOf('timeout', context, function(err) {
|
||||
if (!err) {
|
||||
|
@ -89,7 +106,7 @@ TransactionMixin.beginTransaction = function(options, cb) {
|
|||
});
|
||||
} else {
|
||||
process.nextTick(function() {
|
||||
var err = new Error('Transaction is not supported');
|
||||
const err = new Error(g.f('{{Transaction}} is not supported'));
|
||||
cb(err);
|
||||
});
|
||||
}
|
||||
|
@ -100,73 +117,93 @@ TransactionMixin.beginTransaction = function(options, cb) {
|
|||
if (Transaction) {
|
||||
jutil.mixin(Transaction.prototype, ObserverMixin);
|
||||
/**
|
||||
* Commit a transaction and release it back to the pool
|
||||
* @param {Function} cb Callback function
|
||||
* @returns {Promise|undefined}
|
||||
* Commit a transaction and release it back to the pool.
|
||||
*
|
||||
* Example:
|
||||
*
|
||||
* ```js
|
||||
* MyModel.beginTransaction('READ COMMITTED', function(err, tx) {
|
||||
* // some crud operation of your choice
|
||||
* tx.commit(function(err) {
|
||||
* // release the connection pool upon committing
|
||||
* tx.close(err);
|
||||
* });
|
||||
* });
|
||||
* ```
|
||||
*
|
||||
* @callback {Function} cb Callback function.
|
||||
* @returns {Promise|undefined} Returns a callback promise.
|
||||
*/
|
||||
Transaction.prototype.commit = function(cb) {
|
||||
var self = this;
|
||||
cb = cb || utils.createPromiseCallback();
|
||||
// Report an error if the transaction is not active
|
||||
if (!self.connection) {
|
||||
process.nextTick(function() {
|
||||
cb(new Error('The transaction is not active: ' + self.id));
|
||||
});
|
||||
return cb.promise;
|
||||
if (this.ensureActive(cb)) {
|
||||
const context = {
|
||||
transaction: this,
|
||||
operation: 'commit',
|
||||
};
|
||||
this.notifyObserversAround('commit', context,
|
||||
done => {
|
||||
this.connector.commit(this.connection, done);
|
||||
},
|
||||
err => {
|
||||
// Deference the connection to mark the transaction is not active
|
||||
// The connection should have been released back the pool
|
||||
this.connection = null;
|
||||
cb(err);
|
||||
});
|
||||
}
|
||||
var context = {
|
||||
transaction: self,
|
||||
operation: 'commit'
|
||||
};
|
||||
|
||||
function work(done) {
|
||||
self.connector.commit(self.connection, done);
|
||||
}
|
||||
|
||||
self.notifyObserversAround('commit', context, work, function(err) {
|
||||
// Deference the connection to mark the transaction is not active
|
||||
// The connection should have been released back the pool
|
||||
self.connection = null;
|
||||
cb(err);
|
||||
});
|
||||
|
||||
return cb.promise;
|
||||
};
|
||||
|
||||
/**
|
||||
* Rollback a transaction and release it back to the pool
|
||||
* @param {Function} cb Callback function
|
||||
* @returns {Promise|undefined}
|
||||
* Rollback a transaction and release it back to the pool.
|
||||
*
|
||||
* Example:
|
||||
*
|
||||
* ```js
|
||||
* MyModel.beginTransaction('READ COMMITTED', function(err, tx) {
|
||||
* // some crud operation of your choice
|
||||
* tx.rollback(function(err) {
|
||||
* // release the connection pool upon committing
|
||||
* tx.close(err);
|
||||
* });
|
||||
* });
|
||||
* ```
|
||||
*
|
||||
* @callback {Function} cb Callback function.
|
||||
* @returns {Promise|undefined} Returns a callback promise.
|
||||
*/
|
||||
Transaction.prototype.rollback = function(cb) {
|
||||
var self = this;
|
||||
cb = cb || utils.createPromiseCallback();
|
||||
// Report an error if the transaction is not active
|
||||
if (!self.connection) {
|
||||
process.nextTick(function() {
|
||||
cb(new Error('The transaction is not active: ' + self.id));
|
||||
});
|
||||
return cb.promise;
|
||||
if (this.ensureActive(cb)) {
|
||||
const context = {
|
||||
transaction: this,
|
||||
operation: 'rollback',
|
||||
};
|
||||
this.notifyObserversAround('rollback', context,
|
||||
done => {
|
||||
this.connector.rollback(this.connection, done);
|
||||
},
|
||||
err => {
|
||||
// Deference the connection to mark the transaction is not active
|
||||
// The connection should have been released back the pool
|
||||
this.connection = null;
|
||||
cb(err);
|
||||
});
|
||||
}
|
||||
var context = {
|
||||
transaction: self,
|
||||
operation: 'rollback'
|
||||
};
|
||||
|
||||
function work(done) {
|
||||
self.connector.rollback(self.connection, done);
|
||||
}
|
||||
|
||||
self.notifyObserversAround('rollback', context, work, function(err) {
|
||||
// Deference the connection to mark the transaction is not active
|
||||
// The connection should have been released back the pool
|
||||
self.connection = null;
|
||||
cb(err);
|
||||
});
|
||||
|
||||
return cb.promise;
|
||||
};
|
||||
|
||||
Transaction.prototype.ensureActive = function(cb) {
|
||||
// Report an error if the transaction is not active
|
||||
if (!this.connection) {
|
||||
process.nextTick(() => {
|
||||
cb(new Error(g.f('The {{transaction}} is not active: %s', this.id)));
|
||||
});
|
||||
}
|
||||
return !!this.connection;
|
||||
};
|
||||
|
||||
Transaction.prototype.toJSON = function() {
|
||||
return this.id;
|
||||
};
|
||||
|
@ -177,5 +214,3 @@ if (Transaction) {
|
|||
}
|
||||
|
||||
TransactionMixin.Transaction = Transaction;
|
||||
|
||||
|
||||
|
|
30
lib/types.js
30
lib/types.js
|
@ -1,4 +1,11 @@
|
|||
var Types = {};
|
||||
// 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';
|
||||
|
||||
const Types = {};
|
||||
/**
|
||||
* Schema types
|
||||
*/
|
||||
|
@ -9,7 +16,7 @@ Types.Text = function Text(value) {
|
|||
this.value = value;
|
||||
}; // Text type
|
||||
|
||||
Types.Text.prototype.toObject = Types.Text.prototype.toJSON = function () {
|
||||
Types.Text.prototype.toObject = Types.Text.prototype.toJSON = function() {
|
||||
return this.value;
|
||||
};
|
||||
|
||||
|
@ -19,7 +26,7 @@ Types.JSON = function JSON(value) {
|
|||
}
|
||||
this.value = value;
|
||||
}; // JSON Object
|
||||
Types.JSON.prototype.toObject = Types.JSON.prototype.toJSON = function () {
|
||||
Types.JSON.prototype.toObject = Types.JSON.prototype.toJSON = function() {
|
||||
return this.value;
|
||||
};
|
||||
|
||||
|
@ -29,23 +36,23 @@ Types.Any = function Any(value) {
|
|||
}
|
||||
this.value = value;
|
||||
}; // Any Type
|
||||
Types.Any.prototype.toObject = Types.Any.prototype.toJSON = function () {
|
||||
Types.Any.prototype.toObject = Types.Any.prototype.toJSON = function() {
|
||||
return this.value;
|
||||
};
|
||||
|
||||
module.exports = function (modelTypes) {
|
||||
module.exports = function(modelTypes) {
|
||||
const DateString = require('./date-string');
|
||||
const GeoPoint = require('./geo').GeoPoint;
|
||||
|
||||
var GeoPoint = require('./geo').GeoPoint;
|
||||
|
||||
for(var t in Types) {
|
||||
for (const t in Types) {
|
||||
modelTypes[t] = Types[t];
|
||||
}
|
||||
|
||||
modelTypes.schemaTypes = {};
|
||||
modelTypes.registerType = function (type, names) {
|
||||
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;
|
||||
}
|
||||
};
|
||||
|
@ -58,10 +65,11 @@ module.exports = function (modelTypes) {
|
|||
modelTypes.registerType(Number);
|
||||
modelTypes.registerType(Boolean);
|
||||
modelTypes.registerType(Date);
|
||||
modelTypes.registerType(DateString);
|
||||
modelTypes.registerType(Buffer, ['Binary']);
|
||||
modelTypes.registerType(Array);
|
||||
modelTypes.registerType(GeoPoint);
|
||||
modelTypes.registerType(Object);
|
||||
};
|
||||
|
||||
module.exports.Types = Types;
|
||||
module.exports.Types = Types;
|
||||
|
|
604
lib/utils.js
604
lib/utils.js
|
@ -1,9 +1,17 @@
|
|||
// 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 = mergeSettings;
|
||||
exports.mergeSettings = exports.deepMerge = deepMerge;
|
||||
exports.deepMergeProperty = deepMergeProperty;
|
||||
exports.isPlainObject = isPlainObject;
|
||||
exports.defineCachedRelations = defineCachedRelations;
|
||||
exports.sortObjectsByIds = sortObjectsByIds;
|
||||
|
@ -16,17 +24,38 @@ exports.toRegExp = toRegExp;
|
|||
exports.hasRegExpFlags = hasRegExpFlags;
|
||||
exports.idEquals = idEquals;
|
||||
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 traverse = require('traverse');
|
||||
var assert = require('assert');
|
||||
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 {
|
||||
return require(module);
|
||||
} catch (e) {
|
||||
console.log('Run "npm install loopback-datasource-juggler ' + module
|
||||
+ '" command to use loopback-datasource-juggler using ' + module
|
||||
+ ' database engine');
|
||||
g.log('Run "{{npm install loopback-datasource-juggler}} %s" command ',
|
||||
'to use {{loopback-datasource-juggler}} using %s database engine',
|
||||
module, module);
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
@ -39,20 +68,20 @@ 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];
|
||||
if (typeof val !== 'object' || val instanceof prop.type
|
||||
|| prop.type.name === 'ObjectID') // MongoDB key
|
||||
{
|
||||
const val = where[i];
|
||||
if (typeof val !== 'object' || val instanceof prop.type ||
|
||||
prop.type.name === 'ObjectID' || // MongoDB key
|
||||
prop.type.name === 'uuidFromString') { // C*
|
||||
// Only pick the {propertyName: propertyValue}
|
||||
data[i] = where[i];
|
||||
}
|
||||
|
@ -69,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);
|
||||
|
@ -107,32 +136,31 @@ function mergeIncludes(destination, source) {
|
|||
*/
|
||||
function convertToArray(include) {
|
||||
if (typeof include === 'string') {
|
||||
var obj = {};
|
||||
const obj = {};
|
||||
obj[include] = true;
|
||||
return [obj];
|
||||
} else if (isPlainObject(include)) {
|
||||
//if include is of the form - {relation:'',scope:''}
|
||||
// if include is of the form - {relation:'',scope:''}
|
||||
if (include.rel || include.relation) {
|
||||
return [include];
|
||||
}
|
||||
// Build an array of key/value pairs
|
||||
var newInclude = [];
|
||||
for (var key in include) {
|
||||
var obj = {};
|
||||
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') {
|
||||
var obj = {};
|
||||
const obj = {};
|
||||
obj[includeEntry] = true;
|
||||
normalized.push(obj)
|
||||
}
|
||||
else{
|
||||
normalized.push(obj);
|
||||
} else {
|
||||
normalized.push(includeEntry);
|
||||
}
|
||||
}
|
||||
|
@ -169,17 +197,16 @@ function mergeQuery(base, update, spec) {
|
|||
if (!base.include) {
|
||||
base.include = update.include;
|
||||
} else {
|
||||
if (spec.nestedInclude === true){
|
||||
//specify nestedInclude=true to force nesting of inclusions on scoped
|
||||
//queries. e.g. In physician.patients.getAsync({include: 'address'}),
|
||||
//inclusion should be on patient model, not on physician model.
|
||||
var saved = base.include;
|
||||
if (spec.nestedInclude === true) {
|
||||
// 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.
|
||||
const saved = base.include;
|
||||
base.include = {};
|
||||
base.include[update.include] = saved;
|
||||
}
|
||||
else{
|
||||
//default behaviour of inclusion merge - merge inclusions at the same
|
||||
//level. - https://github.com/strongloop/loopback-datasource-juggler/pull/569#issuecomment-95310874
|
||||
} else {
|
||||
// default behaviour of inclusion merge - merge inclusions at the same
|
||||
// level. - https://github.com/strongloop/loopback-datasource-juggler/pull/569#issuecomment-95310874
|
||||
base.include = mergeIncludes(base.include, update.include);
|
||||
}
|
||||
}
|
||||
|
@ -206,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;
|
||||
|
@ -230,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];
|
||||
|
@ -240,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]) {
|
||||
|
@ -257,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]);
|
||||
result.splice(index, 1);
|
||||
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) {
|
||||
|
@ -278,11 +305,11 @@ function fieldsToArray(fields, properties, excludeUnknown) {
|
|||
|
||||
function selectFields(fields) {
|
||||
// map function
|
||||
return function (obj) {
|
||||
var result = {};
|
||||
var key;
|
||||
return function(obj) {
|
||||
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];
|
||||
|
@ -291,46 +318,144 @@ 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('Unexpected `undefined` in query');
|
||||
break;
|
||||
throw new Error(g.f('Unexpected `undefined` in query'));
|
||||
case 'ignore':
|
||||
default:
|
||||
this.remove();
|
||||
}
|
||||
}
|
||||
|
||||
if (!Array.isArray(x) && (typeof x === 'object' && x !== null
|
||||
&& x.constructor !== Object)) {
|
||||
if (!Array.isArray(x) && (typeof x === 'object' && x !== null &&
|
||||
x.constructor !== Object)) {
|
||||
// This object is not a plain object
|
||||
this.update(x, true); // Stop navigating into this object
|
||||
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
|
||||
|
@ -341,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];
|
||||
}
|
||||
}
|
||||
|
@ -360,52 +485,64 @@ function parseSettings(urlStr) {
|
|||
}
|
||||
|
||||
/**
|
||||
* Merge model settings
|
||||
* Objects deep merge
|
||||
*
|
||||
* Folked from https://github.com/nrf110/deepmerge/blob/master/index.js
|
||||
* Forked from https://github.com/nrf110/deepmerge/blob/master/index.js
|
||||
*
|
||||
* The original function tries to merge array items if they are objects
|
||||
* The original function tries to merge array items if they are objects, this
|
||||
* was changed to always push new items in arrays, independently of their type.
|
||||
*
|
||||
* @param {Object} target The target settings object
|
||||
* @param {Object} src The source settings object
|
||||
* @returns {Object} The merged settings object
|
||||
* NOTE: The function operates as a deep clone when called with a single object
|
||||
* argument.
|
||||
*
|
||||
* @param {Object} base The base object
|
||||
* @param {Object} extras The object to merge with base
|
||||
* @returns {Object} The merged object
|
||||
*/
|
||||
function mergeSettings(target, src) {
|
||||
var array = Array.isArray(src);
|
||||
var dst = array && [] || {};
|
||||
function deepMerge(base, extras) {
|
||||
// deepMerge allows undefined extras to allow deep cloning of arrays
|
||||
const array = Array.isArray(base) && (Array.isArray(extras) || !extras);
|
||||
let dst = array && [] || {};
|
||||
|
||||
if (array) {
|
||||
target = target || [];
|
||||
// Add items from target into dst
|
||||
dst = dst.concat(target);
|
||||
// Add non-existent items from source into dst
|
||||
src.forEach(function(e) {
|
||||
// extras or base is an array
|
||||
extras = extras || [];
|
||||
// Add items from base into dst
|
||||
dst = dst.concat(base);
|
||||
// Add non-existent items from extras into dst
|
||||
extras.forEach(function(e) {
|
||||
if (dst.indexOf(e) === -1) {
|
||||
dst.push(e);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
if (target != null && typeof target === 'object') {
|
||||
// Add properties from target to dst
|
||||
Object.keys(target).forEach(function(key) {
|
||||
dst[key] = target[key];
|
||||
if (base != null && typeof base === 'object') {
|
||||
// Add properties from base to dst
|
||||
Object.keys(base).forEach(function(key) {
|
||||
if (base[key] && typeof base[key] === 'object') {
|
||||
// call deepMerge on nested object to operate a deep clone
|
||||
dst[key] = deepMerge(base[key]);
|
||||
} else {
|
||||
dst[key] = base[key];
|
||||
}
|
||||
});
|
||||
}
|
||||
if (src != null && typeof src === 'object') {
|
||||
// Source is an object
|
||||
Object.keys(src).forEach(function(key) {
|
||||
var srcValue = src[key];
|
||||
if (srcValue == null || typeof srcValue !== 'object') {
|
||||
// The source item value is null, undefined or not an object
|
||||
dst[key] = srcValue;
|
||||
if (extras != null && typeof extras === 'object') {
|
||||
// extras is an object {}
|
||||
Object.keys(extras).forEach(function(key) {
|
||||
const extra = extras[key];
|
||||
if (extra == null || typeof extra !== 'object') {
|
||||
// extra item value is null, undefined or not an object
|
||||
dst[key] = extra;
|
||||
} else {
|
||||
// The source item value is an object
|
||||
if (target == null || typeof target !== 'object' ||
|
||||
target[key] == null) {
|
||||
// If target is not an object or target item value
|
||||
dst[key] = srcValue;
|
||||
// The extra item value is an object
|
||||
if (base == null || typeof base !== 'object' ||
|
||||
base[key] == null) {
|
||||
// base is not an object or base item value is undefined or null
|
||||
dst[key] = extra;
|
||||
} else {
|
||||
dst[key] = mergeSettings(target[key], src[key]);
|
||||
// call deepMerge on nested object
|
||||
dst[key] = deepMerge(base[key], extra);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
@ -415,6 +552,57 @@ function mergeSettings(target, src) {
|
|||
return dst;
|
||||
}
|
||||
|
||||
/**
|
||||
* Properties deep merge
|
||||
* Similar as deepMerge but also works on single properties of any type
|
||||
*
|
||||
* @param {Object} base The base property
|
||||
* @param {Object} extras The property to merge with base
|
||||
* @returns {Object} The merged property
|
||||
*/
|
||||
function deepMergeProperty(base, extras) {
|
||||
const mergedObject = deepMerge({key: base}, {key: extras});
|
||||
const mergedProperty = mergedObject.key;
|
||||
return mergedProperty;
|
||||
}
|
||||
|
||||
const numberIsFinite = Number.isFinite || function(value) {
|
||||
return typeof value === 'number' && isFinite(value);
|
||||
};
|
||||
|
||||
/**
|
||||
* Adds a property __rank to array elements of type object {}
|
||||
* If an inner element already has the __rank property it is not altered
|
||||
* NOTE: the function mutates the provided array
|
||||
*
|
||||
* @param array The original array
|
||||
* @param rank The rank to apply to array elements
|
||||
* @return rankedArray The original array with newly ranked elements
|
||||
*/
|
||||
function rankArrayElements(array, rank) {
|
||||
if (!Array.isArray(array) || !numberIsFinite(rank))
|
||||
return array;
|
||||
|
||||
array.forEach(function(el) {
|
||||
// only apply ranking on objects {} in array
|
||||
if (!el || typeof el != 'object' || Array.isArray(el))
|
||||
return;
|
||||
|
||||
// property rank is already defined for array element
|
||||
if (el.__rank)
|
||||
return;
|
||||
|
||||
// define rank property as non-enumerable and read-only
|
||||
Object.defineProperty(el, '__rank', {
|
||||
writable: false,
|
||||
enumerable: false,
|
||||
configurable: false,
|
||||
value: rank,
|
||||
});
|
||||
});
|
||||
return array;
|
||||
}
|
||||
|
||||
/**
|
||||
* Define an non-enumerable __cachedRelations property
|
||||
* @param {Object} obj The obj to receive the __cachedRelations
|
||||
|
@ -425,48 +613,46 @@ function defineCachedRelations(obj) {
|
|||
writable: true,
|
||||
enumerable: false,
|
||||
configurable: true,
|
||||
value: {}
|
||||
value: {},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the argument is plain object
|
||||
* @param {*) obj The obj value
|
||||
* @param {*} obj The obj value
|
||||
* @returns {boolean}
|
||||
*/
|
||||
function isPlainObject(obj) {
|
||||
return (typeof obj === 'object') && (obj !== null)
|
||||
&& (obj.constructor === Object);
|
||||
return (typeof obj === 'object') && (obj !== null) &&
|
||||
(obj.constructor === Object);
|
||||
}
|
||||
|
||||
|
||||
|
||||
function sortObjectsByIds(idName, ids, objects, strict) {
|
||||
ids = ids.map(function(id) {
|
||||
return (typeof id === 'object') ? String(id) : id;
|
||||
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;
|
||||
|
@ -474,21 +660,12 @@ function sortObjectsByIds(idName, ids, objects, strict) {
|
|||
});
|
||||
|
||||
return heading.concat(tailing);
|
||||
};
|
||||
}
|
||||
|
||||
function createPromiseCallback() {
|
||||
var cb;
|
||||
|
||||
if (!global.Promise) {
|
||||
cb = function(){};
|
||||
cb.promise = {};
|
||||
Object.defineProperty(cb.promise, 'then', { get: throwPromiseNotDefined });
|
||||
Object.defineProperty(cb.promise, 'catch', { get: throwPromiseNotDefined });
|
||||
return cb;
|
||||
}
|
||||
|
||||
var promise = new Promise(function (resolve, reject) {
|
||||
cb = function (err, data) {
|
||||
let cb;
|
||||
const promise = new Promise(function(resolve, reject) {
|
||||
cb = function(err, data) {
|
||||
if (err) return reject(err);
|
||||
return resolve(data);
|
||||
};
|
||||
|
@ -497,10 +674,10 @@ function createPromiseCallback() {
|
|||
return cb;
|
||||
}
|
||||
|
||||
function throwPromiseNotDefined() {
|
||||
throw new Error(
|
||||
'Your Node runtime does support ES6 Promises. ' +
|
||||
'Set "global.Promise" to your preferred implementation of promises.');
|
||||
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');
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -509,13 +686,16 @@ function throwPromiseNotDefined() {
|
|||
* @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');
|
||||
for (var i = 0, n = a.length; i < n; i++) {
|
||||
if (a.indexOf(a[i]) === 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]);
|
||||
}
|
||||
}
|
||||
|
@ -528,12 +708,12 @@ 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('Invalid argument, must be a string, regex literal, or ' +
|
||||
'RegExp object');
|
||||
return new Error(g.f('Invalid argument, must be a string, {{regex}} literal, or ' +
|
||||
'{{RegExp}} object'));
|
||||
|
||||
if (isRegExp)
|
||||
return regex;
|
||||
|
@ -542,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('Invalid regex flags: ' + invalidFlags);
|
||||
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(''));
|
||||
}
|
||||
|
||||
|
@ -593,9 +773,123 @@ 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;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an object that queries targetIds.
|
||||
* @param {Array} The array of targetData
|
||||
* @param {String} The Id property name of target model
|
||||
* @returns {Object} The object that queries targetIds
|
||||
*/
|
||||
function collectTargetIds(targetData, idPropertyName) {
|
||||
const targetIds = [];
|
||||
for (let i = 0; i < targetData.length; i++) {
|
||||
const targetId = targetData[i][idPropertyName];
|
||||
targetIds.push(targetId);
|
||||
}
|
||||
const IdQuery = {
|
||||
inq: uniq(targetIds),
|
||||
};
|
||||
return IdQuery;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find the idKey of a Model.
|
||||
* @param {ModelConstructor} m - Model Constructor
|
||||
* @returns {String}
|
||||
*/
|
||||
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,5 +1,13 @@
|
|||
var util = require('util');
|
||||
var extend = util._extend;
|
||||
// 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';
|
||||
|
||||
const g = require('strong-globalize')();
|
||||
const util = require('util');
|
||||
const extend = util._extend;
|
||||
|
||||
/*!
|
||||
* Module exports
|
||||
|
@ -24,6 +32,7 @@ function Validatable() {
|
|||
|
||||
/**
|
||||
* Validate presence of one or more specified properties.
|
||||
*
|
||||
* Requires a model to include a property to be considered valid; fails when validated field is blank.
|
||||
*
|
||||
* For example, validate presence of title
|
||||
|
@ -40,34 +49,43 @@ function Validatable() {
|
|||
* ```
|
||||
*
|
||||
* @param {String} propertyName One or more property names.
|
||||
* @options {Object} errMsg Optional custom error message. Default is "can't be blank"
|
||||
* @options {Object} options Configuration parameters; see below.
|
||||
* @property {String} message Error message to use instead of default.
|
||||
* @property {String} if Validate only if `if` exists.
|
||||
* @property {String} unless Validate only if `unless` exists.
|
||||
*/
|
||||
Validatable.validatesPresenceOf = getConfigurator('presence');
|
||||
|
||||
/**
|
||||
* Validate absence of one or more specified properties.
|
||||
* A model should not include a property to be considered valid; fails when validated field not blank.
|
||||
*
|
||||
* A model should not include a property to be considered valid; fails when validated field is not blank.
|
||||
*
|
||||
* For example, validate absence of reserved
|
||||
* ```
|
||||
* Post.validatesAbsenceOf('reserved', { unless: 'special' });
|
||||
* ```
|
||||
*
|
||||
* @param {String} propertyName One or more property names.
|
||||
* @options {Object} errMsg Optional custom error message. Default is "can't be set"
|
||||
* @options {Object} options Configuration parameters; see below.
|
||||
* @property {String} message Error message to use instead of default.
|
||||
* @property {String} if Validate only if `if` exists.
|
||||
* @property {String} unless Validate only if `unless` exists.
|
||||
*/
|
||||
Validatable.validatesAbsenceOf = getConfigurator('absence');
|
||||
|
||||
/**
|
||||
* Validate length. Require a property length to be within a specified range.
|
||||
* Three kinds of validations: min, max, is.
|
||||
* Validate length.
|
||||
*
|
||||
* Require a property length to be within a specified range.
|
||||
*
|
||||
* There are three kinds of validations: min, max, is.
|
||||
*
|
||||
* Default error messages:
|
||||
*
|
||||
* - min: too short
|
||||
* - max: too long
|
||||
* - is: length is wrong
|
||||
* - is: length is wrong
|
||||
*
|
||||
* Example: length validations
|
||||
* ```
|
||||
|
@ -81,36 +99,42 @@ Validatable.validatesAbsenceOf = getConfigurator('absence');
|
|||
* User.validatesLengthOf('password', {min: 7, message: {min: 'too weak'}});
|
||||
* User.validatesLengthOf('state', {is: 2, message: {is: 'is not valid state name'}});
|
||||
* ```
|
||||
*
|
||||
* @param {String} propertyName Property name to validate.
|
||||
* @options {Object} Options See below.
|
||||
* @options {Object} options Configuration parameters; see below.
|
||||
* @property {Number} is Value that property must equal to validate.
|
||||
* @property {Number} min Value that property must be less than to be valid.
|
||||
* @property {Number} max Value that property must be less than to be valid.
|
||||
* @property {Object} message Optional Object with string properties for custom error message for each validation: is, min, or max
|
||||
* @property {Object} message Optional object with string properties for custom error message for each validation: is, min, or max.
|
||||
*/
|
||||
Validatable.validatesLengthOf = getConfigurator('length');
|
||||
|
||||
/**
|
||||
* Validate numericality. Requires a value for property to be either an integer or number.
|
||||
* Validate numericality.
|
||||
*
|
||||
* Requires a value for property to be either an integer or number.
|
||||
*
|
||||
* Example
|
||||
* ```
|
||||
* User.validatesNumericalityOf('age', { message: { number: '...' }});
|
||||
* User.validatesNumericalityOf('age', {int: true, message: { int: '...' }});
|
||||
* User.validatesNumericalityOf('age', { message: { number: 'is not a number' }});
|
||||
* User.validatesNumericalityOf('age', {int: true, message: { int: 'is not an integer' }});
|
||||
* ```
|
||||
*
|
||||
* @param {String} propertyName Property name to validate.
|
||||
* @options {Object} Options See below.
|
||||
* @options {Object} options Configuration parameters; see below.
|
||||
* @property {Boolean} int If true, then property must be an integer to be valid.
|
||||
* @property {Object} message Optional object with string properties for 'int' for integer validation. Default error messages:
|
||||
*
|
||||
* @property {Boolean} allowBlank Allow property to be blank.
|
||||
* @property {Boolean} allowNull Allow property to be null.
|
||||
* @property {Object} message Optional object with string properties for 'int' for integer validation. Default error messages:
|
||||
* - number: is not a number
|
||||
* - int: is not an integer
|
||||
*/
|
||||
Validatable.validatesNumericalityOf = getConfigurator('numericality');
|
||||
|
||||
/**
|
||||
* Validate inclusion in set. Require a value for property to be in the specified array.
|
||||
* Validate inclusion in set.
|
||||
*
|
||||
* Require a value for property to be in the specified array.
|
||||
*
|
||||
* Example:
|
||||
* ```
|
||||
|
@ -121,34 +145,41 @@ Validatable.validatesNumericalityOf = getConfigurator('numericality');
|
|||
* ```
|
||||
*
|
||||
* @param {String} propertyName Property name to validate.
|
||||
* @options {Object} Options
|
||||
* @property {Array} inArray Property must match one of the values in the array to be valid.
|
||||
* @property {String} message Optional error message if property is not valid. Default error message: "is not included in the list".
|
||||
* @options {Object} options Configuration parameters; see below.
|
||||
* @property {Array} in Property must match one of the values in the array to be valid.
|
||||
* @property {String} message Optional error message if property is not valid.
|
||||
* Default error message: "is not included in the list".
|
||||
* @property {Boolean} allowNull Whether null values are allowed.
|
||||
*/
|
||||
Validatable.validatesInclusionOf = getConfigurator('inclusion');
|
||||
|
||||
/**
|
||||
* Validate exclusion. Require a property value not be in the specified array.
|
||||
* Validate exclusion in a set.
|
||||
*
|
||||
* Require a property value not be in the specified array.
|
||||
*
|
||||
* Example: `Company.validatesExclusionOf('domain', {in: ['www', 'admin']});`
|
||||
*
|
||||
* @param {String} propertyName Property name to validate.
|
||||
* @options {Object} Options
|
||||
* @property {Array} inArray Property must match one of the values in the array to be valid.
|
||||
* @property {String} message Optional error message if property is not valid. Default error message: "is reserved".
|
||||
* @options {Object} options Configuration parameters; see below.
|
||||
* @property {Array} in Property must not match any of the values in the array to be valid.
|
||||
* @property {String} message Optional error message if property is not valid. Default error message: "is reserved".
|
||||
* @property {Boolean} allowNull Whether null values are allowed.
|
||||
*/
|
||||
Validatable.validatesExclusionOf = getConfigurator('exclusion');
|
||||
|
||||
/**
|
||||
* Validate format. Require a model to include a property that matches the given format.
|
||||
* Validate format.
|
||||
*
|
||||
* Require a model to include a property that matches the given format. Example:
|
||||
* `User.validatesFormat('name', {with: /\w+/});`
|
||||
* Require a model to include a property that matches the given format.
|
||||
*
|
||||
* Example: `User.validatesFormatOf('name', {with: /\w+/});`
|
||||
*
|
||||
* @param {String} propertyName Property name to validate.
|
||||
* @options {Object} Options
|
||||
* @options {Object} options Configuration parameters; see below.
|
||||
* @property {RegExp} with Regular expression to validate format.
|
||||
* @property {String} message Optional error message if property is not valid. Default error message: " is invalid".
|
||||
* @property {String} message Optional error message if property is not valid. Default error message: " is invalid".
|
||||
* @property {Boolean} allowNull Whether null values are allowed.
|
||||
*/
|
||||
Validatable.validatesFormatOf = getConfigurator('format');
|
||||
|
||||
|
@ -156,7 +187,7 @@ Validatable.validatesFormatOf = getConfigurator('format');
|
|||
* Validate using custom validation function.
|
||||
*
|
||||
* Example:
|
||||
*
|
||||
*```javascript
|
||||
* User.validate('name', customValidator, {message: 'Bad name'});
|
||||
* function customValidator(err) {
|
||||
* if (this.name === 'bad') err();
|
||||
|
@ -165,18 +196,19 @@ Validatable.validatesFormatOf = getConfigurator('format');
|
|||
* user.isValid(); // true
|
||||
* user.name = 'bad';
|
||||
* user.isValid(); // false
|
||||
* ```
|
||||
*
|
||||
* @param {String} propertyName Property name to validate.
|
||||
* @param {Function} validatorFn Custom validation function.
|
||||
* @options {Object} Options See below.
|
||||
* @property {String} message Optional error message if property is not valid. Default error message: " is invalid".
|
||||
* @options {Object} options Configuration parameters; see below.
|
||||
* @property {String} message Optional error message if property is not valid. Default error message: " is invalid".
|
||||
* @property {Boolean} allowNull Whether null values are allowed.
|
||||
*/
|
||||
Validatable.validate = getConfigurator('custom');
|
||||
|
||||
/**
|
||||
* Validate using custom asynchronous validation function.
|
||||
*
|
||||
*
|
||||
* Example:
|
||||
*```js
|
||||
* User.validateAsync('name', customValidator, {message: 'Bad name'});
|
||||
|
@ -196,16 +228,19 @@ Validatable.validate = getConfigurator('custom');
|
|||
* user.isValid(function (isValid) {
|
||||
* isValid; // false
|
||||
* })
|
||||
*```
|
||||
* ```
|
||||
*
|
||||
* @param {String} propertyName Property name to validate.
|
||||
* @param {Function} validatorFn Custom validation function.
|
||||
* @options {Object} Options See below
|
||||
* @property {String} message Optional error message if property is not valid. Default error message: " is invalid".
|
||||
* @options {Object} options Configuration parameters; see below.
|
||||
* @property {String} message Optional error message if property is not valid. Default error message: " is invalid".
|
||||
* @property {Boolean} allowNull Whether null values are allowed.
|
||||
*/
|
||||
Validatable.validateAsync = getConfigurator('custom', {async: true});
|
||||
|
||||
/**
|
||||
* Validate uniqueness. Ensure the value for property is unique in the collection of models.
|
||||
* Validate uniqueness of the value for a property in the collection of models.
|
||||
*
|
||||
* Not available for all connectors. Currently supported with these connectors:
|
||||
* - In Memory
|
||||
* - Oracle
|
||||
|
@ -219,21 +254,39 @@ Validatable.validateAsync = getConfigurator('custom', {async: true});
|
|||
* // The login must be unique within each Site.
|
||||
* SiteUser.validateUniquenessOf('login', { scopedTo: ['siteId'] });
|
||||
* ```
|
||||
|
||||
*
|
||||
* @param {String} propertyName Property name to validate.
|
||||
* @options {Object} Options See below.
|
||||
* @options {Object} options Configuration parameters; see below.
|
||||
* @property {RegExp} with Regular expression to validate format.
|
||||
* @property {Array.<String>} scopedTo List of properties defining the scope.
|
||||
* @property {String} message Optional error message if property is not valid. Default error message: "is not unique".
|
||||
* @property {String} message Optional error message if property is not valid. Default error message: "is not unique".
|
||||
* @property {Boolean} allowNull Whether null values are allowed.
|
||||
* @property {String} ignoreCase Make the validation case insensitive.
|
||||
* @property {String} if Validate only if `if` exists.
|
||||
* @property {String} unless Validate only if `unless` exists.
|
||||
*/
|
||||
Validatable.validatesUniquenessOf = getConfigurator('uniqueness', {async: true});
|
||||
|
||||
/**
|
||||
* Validate if a value for a property is a Date.
|
||||
*
|
||||
* Example
|
||||
* ```
|
||||
* User.validatesDateOf('today', {message: 'today is not a date!'});
|
||||
* ```
|
||||
*
|
||||
* @param {String} propertyName Property name to validate.
|
||||
* @options {Object} options Configuration parameters; see below.
|
||||
* @property {String} message Error message to use instead of default.
|
||||
*/
|
||||
Validatable.validatesDateOf = getConfigurator('date');
|
||||
|
||||
// implementation of validators
|
||||
|
||||
/*!
|
||||
* Presence validator
|
||||
*/
|
||||
function validatePresence(attr, conf, err) {
|
||||
function validatePresence(attr, conf, err, options) {
|
||||
if (blank(this[attr])) {
|
||||
err();
|
||||
}
|
||||
|
@ -242,7 +295,7 @@ function validatePresence(attr, conf, err) {
|
|||
/*!
|
||||
* Absence validator
|
||||
*/
|
||||
function validateAbsence(attr, conf, err) {
|
||||
function validateAbsence(attr, conf, err, options) {
|
||||
if (!blank(this[attr])) {
|
||||
err();
|
||||
}
|
||||
|
@ -251,10 +304,10 @@ function validateAbsence(attr, conf, err) {
|
|||
/*!
|
||||
* Length validator
|
||||
*/
|
||||
function validateLength(attr, conf, err) {
|
||||
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');
|
||||
}
|
||||
|
@ -269,10 +322,10 @@ function validateLength(attr, conf, err) {
|
|||
/*!
|
||||
* Numericality validator
|
||||
*/
|
||||
function validateNumericality(attr, conf, err) {
|
||||
function validateNumericality(attr, conf, err, options) {
|
||||
if (nullCheck.call(this, attr, conf, err)) return;
|
||||
|
||||
if (typeof this[attr] !== 'number') {
|
||||
if (typeof this[attr] !== 'number' || isNaN(this[attr])) {
|
||||
return err('number');
|
||||
}
|
||||
if (conf.int && this[attr] !== Math.round(this[attr])) {
|
||||
|
@ -283,33 +336,34 @@ function validateNumericality(attr, conf, err) {
|
|||
/*!
|
||||
* Inclusion validator
|
||||
*/
|
||||
function validateInclusion(attr, conf, err) {
|
||||
function validateInclusion(attr, conf, err, options) {
|
||||
if (nullCheck.call(this, attr, conf, err)) return;
|
||||
|
||||
if (!~conf.in.indexOf(this[attr])) {
|
||||
err()
|
||||
err();
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
* Exclusion validator
|
||||
*/
|
||||
function validateExclusion(attr, conf, err) {
|
||||
function validateExclusion(attr, conf, err, options) {
|
||||
if (nullCheck.call(this, attr, conf, err)) return;
|
||||
|
||||
if (~conf.in.indexOf(this[attr])) {
|
||||
err()
|
||||
err();
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
* Format validator
|
||||
*/
|
||||
function validateFormat(attr, conf, err) {
|
||||
function validateFormat(attr, conf, err, options) {
|
||||
if (nullCheck.call(this, attr, conf, err)) return;
|
||||
|
||||
if (typeof this[attr] === 'string') {
|
||||
if (!this[attr].match(conf['with'])) {
|
||||
if (typeof this[attr] === 'string' || typeof this[attr] === 'number') {
|
||||
const regex = new RegExp(conf['with']);
|
||||
if (!regex.test(this[attr])) {
|
||||
err();
|
||||
}
|
||||
} else {
|
||||
|
@ -320,45 +374,89 @@ function validateFormat(attr, conf, err) {
|
|||
/*!
|
||||
* Custom validator
|
||||
*/
|
||||
function validateCustom(attr, conf, err, done) {
|
||||
conf.customValidator.call(this, err, done);
|
||||
function validateCustom(attr, conf, err, options, done) {
|
||||
if (typeof options === 'function') {
|
||||
done = options;
|
||||
options = {};
|
||||
}
|
||||
if (!done) {
|
||||
// called from a sync validator, stick options on end
|
||||
conf.customValidator.call(this, err, options);
|
||||
} else {
|
||||
if (conf.customValidator.length === 3) {
|
||||
// if they declared the validator with 3 args, they are expecting options
|
||||
conf.customValidator.call(this, err, options, done);
|
||||
} else {
|
||||
// otherwise just pass the expected two (no context)
|
||||
conf.customValidator.call(this, err, done);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function escapeStringRegexp(str) {
|
||||
if (typeof str !== 'string') {
|
||||
throw new TypeError('Expected a string');
|
||||
}
|
||||
const matchOperatorsRe = /[|\\{}()[\]^$+*?.]/g;
|
||||
return str.replace(matchOperatorsRe, '\\$&');
|
||||
}
|
||||
|
||||
/*!
|
||||
* Uniqueness validator
|
||||
*/
|
||||
function validateUniqueness(attr, conf, err, done) {
|
||||
function validateUniqueness(attr, conf, err, options, done) {
|
||||
if (typeof options === 'function') {
|
||||
done = options;
|
||||
options = {};
|
||||
}
|
||||
if (blank(this[attr])) {
|
||||
return process.nextTick(done);
|
||||
}
|
||||
var cond = {where: {}};
|
||||
cond.where[attr] = this[attr];
|
||||
const cond = {where: {}};
|
||||
|
||||
if (conf && conf.ignoreCase) {
|
||||
cond.where[attr] = new RegExp('^' + escapeStringRegexp(this[attr]) + '$', 'i');
|
||||
} else {
|
||||
cond.where[attr] = this[attr];
|
||||
}
|
||||
|
||||
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();
|
||||
this.constructor.find(cond, function (error, found) {
|
||||
const idName = this.constructor.definition.idName();
|
||||
const isNewRecord = this.isNewRecord();
|
||||
this.constructor.find(cond, options, function(error, found) {
|
||||
if (error) {
|
||||
err(error);
|
||||
} else if (found.length > 1) {
|
||||
err();
|
||||
} else if (found.length === 1 && idName === attr && isNewRecord) {
|
||||
err();
|
||||
} else if (found.length === 1 && (!this.id || !found[0].id || found[0].id.toString() != this.id.toString())) {
|
||||
} else if (found.length === 1 && (
|
||||
!this.id || !found[0].id || found[0].id.toString() != this.id.toString()
|
||||
)) {
|
||||
err();
|
||||
}
|
||||
done();
|
||||
}.bind(this));
|
||||
}
|
||||
|
||||
var validators = {
|
||||
/*!
|
||||
* Date validator
|
||||
*/
|
||||
function validateDate(attr, conf, err) {
|
||||
if (this[attr] === null || this[attr] === undefined) return;
|
||||
|
||||
const date = new Date(this[attr]);
|
||||
if (isNaN(date.getTime())) return err();
|
||||
}
|
||||
|
||||
const validators = {
|
||||
presence: validatePresence,
|
||||
absence: validateAbsence,
|
||||
length: validateLength,
|
||||
|
@ -367,12 +465,13 @@ var validators = {
|
|||
exclusion: validateExclusion,
|
||||
format: validateFormat,
|
||||
custom: validateCustom,
|
||||
uniqueness: validateUniqueness
|
||||
uniqueness: validateUniqueness,
|
||||
date: validateDate,
|
||||
};
|
||||
|
||||
function getConfigurator(name, opts) {
|
||||
return function () {
|
||||
var args = Array.prototype.slice.call(arguments);
|
||||
return function() {
|
||||
const args = Array.prototype.slice.call(arguments);
|
||||
args[1] = args[1] || {};
|
||||
configure(this, name, args, opts);
|
||||
};
|
||||
|
@ -387,15 +486,15 @@ function getConfigurator(name, opts) {
|
|||
* NOTE: This method can be called as synchronous only when no asynchronous validation is
|
||||
* configured. It's strongly recommended to run all validations as asyncronous.
|
||||
*
|
||||
* Example: ExpressJS controller: render user if valid, show flash otherwise
|
||||
* ```
|
||||
* Example: ExpressJS controller - render user if valid, show flash otherwise
|
||||
* ```javascript
|
||||
* user.isValid(function (valid) {
|
||||
* if (valid) res.render({user: user});
|
||||
* else res.flash('error', 'User is not valid'), console.log(user.errors), res.redirect('/users');
|
||||
* });
|
||||
* ```
|
||||
* Another example:
|
||||
* ```
|
||||
* ```javascript
|
||||
* user.isValid(function (valid) {
|
||||
* if (!valid) {
|
||||
* console.log(user.errors);
|
||||
|
@ -407,22 +506,26 @@ function getConfigurator(name, opts) {
|
|||
* }
|
||||
* });
|
||||
* ```
|
||||
* @param {Function} callback called with (valid)
|
||||
* @callback {Function} callback Called with (valid).
|
||||
* @param {Object} data Data to be validated.
|
||||
* @param {Object} options Options to be specified upon validation.
|
||||
* @returns {Boolean} True if no asynchronous validation is configured and all properties pass validation.
|
||||
*/
|
||||
Validatable.prototype.isValid = function (callback, data) {
|
||||
var valid = true, inst = this, wait = 0, async = false;
|
||||
var validations = this.constructor.validations;
|
||||
Validatable.prototype.isValid = function(callback, data, options) {
|
||||
options = options || {};
|
||||
let valid = true, wait = 0, async = false;
|
||||
const inst = this;
|
||||
const validations = this.constructor.validations;
|
||||
|
||||
var reportDiscardedProperties = this.__strict === 'validate' &&
|
||||
const reportDiscardedProperties = this.__strict &&
|
||||
this.__unknownProperties && this.__unknownProperties.length;
|
||||
|
||||
// exit with success when no errors
|
||||
if (typeof validations !== 'object' && !reportDiscardedProperties) {
|
||||
cleanErrors(this);
|
||||
if (callback) {
|
||||
this.trigger('validate', function (validationsDone) {
|
||||
validationsDone.call(inst, function () {
|
||||
this.trigger('validate', function(validationsDone) {
|
||||
validationsDone.call(inst, function() {
|
||||
callback(valid);
|
||||
});
|
||||
}, data, callback);
|
||||
|
@ -433,26 +536,26 @@ Validatable.prototype.isValid = function (callback, data) {
|
|||
Object.defineProperty(this, 'errors', {
|
||||
enumerable: false,
|
||||
configurable: true,
|
||||
value: new Errors
|
||||
value: new Errors,
|
||||
});
|
||||
|
||||
this.trigger('validate', function (validationsDone) {
|
||||
var inst = this,
|
||||
asyncFail = false;
|
||||
this.trigger('validate', function(validationsDone) {
|
||||
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;
|
||||
wait += 1;
|
||||
process.nextTick(function () {
|
||||
validationFailed(inst, attr, v, done);
|
||||
process.nextTick(function() {
|
||||
validationFailed(inst, attr, v, options, done);
|
||||
});
|
||||
} else {
|
||||
if (validationFailed(inst, attr, v)) {
|
||||
if (validationFailed(inst, attr, v, options)) {
|
||||
valid = false;
|
||||
}
|
||||
}
|
||||
|
@ -460,17 +563,17 @@ Validatable.prototype.isValid = function (callback, data) {
|
|||
});
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
if (!async) {
|
||||
validationsDone.call(inst, function () {
|
||||
validationsDone.call(inst, function() {
|
||||
if (valid) cleanErrors(inst);
|
||||
if (callback) {
|
||||
callback(valid);
|
||||
|
@ -481,7 +584,7 @@ Validatable.prototype.isValid = function (callback, data) {
|
|||
function done(fail) {
|
||||
asyncFail = asyncFail || fail;
|
||||
if (--wait === 0) {
|
||||
validationsDone.call(inst, function () {
|
||||
validationsDone.call(inst, function() {
|
||||
if (valid && !asyncFail) cleanErrors(inst);
|
||||
if (callback) {
|
||||
callback(valid && !asyncFail);
|
||||
|
@ -489,7 +592,6 @@ Validatable.prototype.isValid = function (callback, data) {
|
|||
});
|
||||
}
|
||||
}
|
||||
|
||||
}, data, callback);
|
||||
|
||||
if (async) {
|
||||
|
@ -499,37 +601,41 @@ Validatable.prototype.isValid = function (callback, data) {
|
|||
} else {
|
||||
return valid;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
function cleanErrors(inst) {
|
||||
Object.defineProperty(inst, 'errors', {
|
||||
enumerable: false,
|
||||
configurable: true,
|
||||
value: false
|
||||
value: false,
|
||||
});
|
||||
}
|
||||
|
||||
function validationFailed(inst, attr, conf, cb) {
|
||||
var opts = conf.options || {};
|
||||
function validationFailed(inst, attr, conf, options, cb) {
|
||||
const opts = conf.options || {};
|
||||
|
||||
if (typeof options === 'function') {
|
||||
cb = options;
|
||||
options = {};
|
||||
}
|
||||
|
||||
if (typeof attr !== 'string') return false;
|
||||
|
||||
// here we should check skip validation conditions (if, unless)
|
||||
// that can be specified in conf
|
||||
if (skipValidation(inst, conf, 'if')
|
||||
|| skipValidation(inst, conf, 'unless')) {
|
||||
if (skipValidation(inst, conf, 'if') ||
|
||||
skipValidation(inst, conf, 'unless')) {
|
||||
if (cb) cb(false);
|
||||
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;
|
||||
}
|
||||
|
@ -553,8 +659,9 @@ function validationFailed(inst, attr, conf, cb) {
|
|||
if (kind !== false) inst.errors.add(attr, message, code);
|
||||
fail = true;
|
||||
});
|
||||
validatorArguments.push(options);
|
||||
if (cb) {
|
||||
validatorArguments.push(function () {
|
||||
validatorArguments.push(function() {
|
||||
cb(fail);
|
||||
});
|
||||
}
|
||||
|
@ -563,7 +670,7 @@ function validationFailed(inst, attr, conf, 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;
|
||||
|
@ -581,38 +688,50 @@ 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',
|
||||
length: {
|
||||
min: 'too short',
|
||||
max: 'too long',
|
||||
is: 'length is wrong'
|
||||
is: 'length is wrong',
|
||||
},
|
||||
common: {
|
||||
blank: 'is blank',
|
||||
'null': 'is null'
|
||||
'null': 'is null',
|
||||
},
|
||||
numericality: {
|
||||
'int': 'is not an integer',
|
||||
'number': 'is not a number'
|
||||
'number': 'is not a number',
|
||||
},
|
||||
inclusion: 'is not included in the list',
|
||||
exclusion: 'is reserved',
|
||||
uniqueness: 'is not unique'
|
||||
uniqueness: 'is not unique',
|
||||
date: 'is not a valid date',
|
||||
};
|
||||
|
||||
/**
|
||||
* Checks if attribute is undefined or null. Calls err function with 'blank' or 'null'.
|
||||
* See defaultMessages. You can affect this behaviour with conf.allowBlank and conf.allowNull.
|
||||
* @private
|
||||
* @param {String} attr Property name of attribute
|
||||
* @param {Object} conf conf object for validator
|
||||
* @param {Function} err
|
||||
* @return {Boolean} returns true if attribute is null or blank
|
||||
*/
|
||||
function nullCheck(attr, conf, err) {
|
||||
if (this[attr] == null) {
|
||||
if (!conf.allowNull) {
|
||||
err('null');
|
||||
// First determine if attribute is defined
|
||||
if (typeof this[attr] === 'undefined' || this[attr] === '') {
|
||||
if (!conf.allowBlank) {
|
||||
err('blank');
|
||||
}
|
||||
return true;
|
||||
} else {
|
||||
if (blank(this[attr])) {
|
||||
if (!conf.allowBlank) {
|
||||
err('blank');
|
||||
// Now check if attribute is null
|
||||
if (this[attr] === null) {
|
||||
if (!conf.allowNull) {
|
||||
err('null');
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
@ -642,11 +761,11 @@ function configure(cls, validation, args, opts) {
|
|||
writable: true,
|
||||
configurable: true,
|
||||
enumerable: false,
|
||||
value: {}
|
||||
value: {},
|
||||
});
|
||||
}
|
||||
args = [].slice.call(args);
|
||||
var conf;
|
||||
let conf;
|
||||
if (typeof args[args.length - 1] === 'object') {
|
||||
conf = args.pop();
|
||||
} else {
|
||||
|
@ -656,9 +775,9 @@ function configure(cls, validation, args, opts) {
|
|||
conf.customValidator = args.pop();
|
||||
}
|
||||
conf.validation = validation;
|
||||
args.forEach(function (attr) {
|
||||
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);
|
||||
|
@ -670,11 +789,11 @@ function Errors() {
|
|||
Object.defineProperty(this, 'codes', {
|
||||
enumerable: false,
|
||||
configurable: true,
|
||||
value: {}
|
||||
value: {},
|
||||
});
|
||||
}
|
||||
|
||||
Errors.prototype.add = function (field, message, code) {
|
||||
Errors.prototype.add = function(field, message, code) {
|
||||
code = code || 'invalid';
|
||||
if (!this[field]) {
|
||||
this[field] = [];
|
||||
|
@ -685,8 +804,8 @@ Errors.prototype.add = function (field, message, code) {
|
|||
};
|
||||
|
||||
function ErrorCodes(messages) {
|
||||
var c = this;
|
||||
Object.keys(messages).forEach(function (field) {
|
||||
const c = this;
|
||||
Object.keys(messages).forEach(function(field) {
|
||||
c[field] = messages[field].codes;
|
||||
});
|
||||
}
|
||||
|
@ -736,21 +855,23 @@ function ErrorCodes(messages) {
|
|||
* }
|
||||
*
|
||||
* // This line shows how to create a ValidationError
|
||||
* err = new ValidationError(model);
|
||||
* var err = new MyModel.ValidationError(model);
|
||||
* callback(err);
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
function ValidationError(obj) {
|
||||
if (!(this instanceof ValidationError)) return new ValidationError(obj);
|
||||
|
||||
this.name = 'ValidationError';
|
||||
|
||||
var context = obj && obj.constructor && obj.constructor.modelName;
|
||||
this.message = util.format(
|
||||
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;
|
||||
|
@ -758,7 +879,7 @@ function ValidationError(obj) {
|
|||
this.details = {
|
||||
context: context,
|
||||
codes: obj.errors && obj.errors.codes,
|
||||
messages: obj.errors
|
||||
messages: obj.errors,
|
||||
};
|
||||
|
||||
if (Error.captureStackTrace) {
|
||||
|
@ -774,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);
|
||||
|
@ -796,19 +917,19 @@ 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) {
|
||||
formattedValue = propertyValue.toISOString();
|
||||
formattedValue = isNaN(propertyValue.getTime()) ? propertyValue.toString() : propertyValue.toISOString();
|
||||
} else if (valueType === 'object') {
|
||||
// objects and arrays
|
||||
formattedValue = util.inspect(propertyValue, {
|
||||
showHidden: false,
|
||||
color: false,
|
||||
// show top-level object properties only
|
||||
depth: Array.isArray(propertyValue) ? 1 : 0
|
||||
depth: Array.isArray(propertyValue) ? 1 : 0,
|
||||
});
|
||||
formattedValue = truncatePropertyString(formattedValue);
|
||||
} else {
|
||||
|
@ -819,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;
|
||||
|
@ -834,5 +955,5 @@ function truncatePropertyString(value) {
|
|||
len -= 3;
|
||||
}
|
||||
|
||||
return value.slice(0, len-4) + '...' + tail;
|
||||
return value.slice(0, len - 4) + '...' + tail;
|
||||
}
|
||||
|
|
File diff suppressed because it is too large
Load Diff
64
package.json
64
package.json
|
@ -1,7 +1,10 @@
|
|||
{
|
||||
"name": "loopback-datasource-juggler",
|
||||
"version": "2.44.0",
|
||||
"description": "LoopBack DataSoure Juggler",
|
||||
"version": "5.0.9",
|
||||
"publishConfig": {
|
||||
"export-tests": true
|
||||
},
|
||||
"description": "LoopBack DataSource Juggler",
|
||||
"keywords": [
|
||||
"StrongLoop",
|
||||
"LoopBack",
|
||||
|
@ -11,37 +14,56 @@
|
|||
"Juggler",
|
||||
"ORM"
|
||||
],
|
||||
"author": "IBM Corp.",
|
||||
"engines": {
|
||||
"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",
|
||||
"browser": {
|
||||
"depd": "./lib/browser.depd.js"
|
||||
},
|
||||
"scripts": {
|
||||
"clean": "make clean",
|
||||
"help": "make help",
|
||||
"refresh": "make refresh",
|
||||
"test": "make test"
|
||||
"lint": "eslint .",
|
||||
"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 lint"
|
||||
},
|
||||
"engines": [
|
||||
"node >= 0.6"
|
||||
],
|
||||
"devDependencies": {
|
||||
"bluebird": "^2.9.9",
|
||||
"mocha": "^2.1.0",
|
||||
"should": "^5.0.0"
|
||||
"@commitlint/cli": "^19.3.0",
|
||||
"@commitlint/config-conventional": "^19.2.2",
|
||||
"@types/node": "^10.17.60",
|
||||
"async-iterators": "^0.2.2",
|
||||
"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": "^10.4.0",
|
||||
"nyc": "^15.1.0",
|
||||
"should": "^13.2.3",
|
||||
"typescript": "^5.4.5"
|
||||
},
|
||||
"dependencies": {
|
||||
"async": "~1.0.0",
|
||||
"debug": "^2.1.1",
|
||||
"depd": "^1.0.0",
|
||||
"inflection": "^1.6.0",
|
||||
"loopback-connector": "^2.1.0",
|
||||
"node-uuid": "^1.4.2",
|
||||
"qs": "^3.1.0",
|
||||
"traverse": "^0.6.6"
|
||||
"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"
|
||||
}
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
{
|
||||
"extends": [
|
||||
"github>loopbackio/cicd//shared-configs/renovate/base"
|
||||
]
|
||||
}
|
|
@ -1,5 +0,0 @@
|
|||
#!/bin/sh
|
||||
|
||||
# travis-ci.org now provides neo4j server but it is not started on boot
|
||||
which neo4j && sudo neo4j start
|
||||
sleep 5
|
|
@ -1,3 +1,10 @@
|
|||
// 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';
|
||||
|
||||
/*
|
||||
* Describe context objects of operation hooks in comprehensive HTML table.
|
||||
* Usage:
|
||||
|
@ -5,64 +12,85 @@
|
|||
* $ 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', 'after save',
|
||||
'before delete', 'after delete'
|
||||
'before save', 'persist', 'loaded', 'after save',
|
||||
'before delete', 'after delete',
|
||||
];
|
||||
|
||||
var dataSources = [
|
||||
const dataSources = [
|
||||
createOptimizedDataSource(),
|
||||
createUnoptimizedDataSource()
|
||||
createUnoptimizedDataSource(),
|
||||
];
|
||||
|
||||
var observedContexts = [];
|
||||
var lastId = 0;
|
||||
const observedContexts = [];
|
||||
let lastId = 0;
|
||||
|
||||
Promise.onPossiblyUnhandledRejection(function(err) {
|
||||
console.error('POSSIBLY UNHANDLED REJECTION', err.stack);
|
||||
});
|
||||
|
||||
var operations = [
|
||||
/* eslint-disable camelcase */
|
||||
const operations = [
|
||||
function find(ds) {
|
||||
return ds.TestModel.find({ where: { id: '1' } });
|
||||
return ds.TestModel.find({where: {id: '1'}});
|
||||
},
|
||||
|
||||
function count(ds) {
|
||||
return ds.TestModel.count({ id: ds.existingInstance.id });
|
||||
return ds.TestModel.count({id: ds.existingInstance.id});
|
||||
},
|
||||
|
||||
function create(ds) {
|
||||
return ds.TestModel.create({ name: 'created' });
|
||||
return ds.TestModel.create({name: 'created'});
|
||||
},
|
||||
|
||||
function findOrCreate_found(ds) {
|
||||
return ds.TestModel.findOrCreate(
|
||||
{ where: { name: ds.existingInstance.name } },
|
||||
{ name: ds.existingInstance.name });
|
||||
{where: {name: ds.existingInstance.name}},
|
||||
{name: ds.existingInstance.name},
|
||||
);
|
||||
},
|
||||
|
||||
function findOrCreate_create(ds) {
|
||||
return ds.TestModel.findOrCreate(
|
||||
{ where: { name: 'new-record' } },
|
||||
{ name: 'new-record' });
|
||||
{where: {name: 'new-record'}},
|
||||
{name: 'new-record'},
|
||||
);
|
||||
},
|
||||
|
||||
function updateOrCreate_create(ds) {
|
||||
return ds.TestModel.updateOrCreate({ id: 'not-found', name: 'not found' });
|
||||
return ds.TestModel.updateOrCreate({id: 'not-found', name: 'not found'});
|
||||
},
|
||||
|
||||
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) {
|
||||
return ds.TestModel.replaceOrCreate({id: 'not-found', name: 'not found'});
|
||||
},
|
||||
|
||||
function replaceOrCreate_update(ds) {
|
||||
return ds.TestModel.replaceOrCreate(
|
||||
{id: ds.existingInstance.id, name: 'new name'},
|
||||
);
|
||||
},
|
||||
|
||||
function replaceById(ds) {
|
||||
return ds.TestModel.replaceById(
|
||||
ds.existingInstance.id,
|
||||
{name: 'new name'},
|
||||
);
|
||||
},
|
||||
|
||||
function updateAll(ds) {
|
||||
return ds.TestModel.updateAll({ name: 'searched' }, { name: 'updated' });
|
||||
return ds.TestModel.updateAll({name: 'searched'}, {name: 'updated'});
|
||||
},
|
||||
|
||||
function prototypeSave(ds) {
|
||||
|
@ -71,7 +99,7 @@ var operations = [
|
|||
},
|
||||
|
||||
function prototypeUpdateAttributes(ds) {
|
||||
return ds.existingInstance.updateAttributes({ name: 'changed' });
|
||||
return ds.existingInstance.updateAttributes({name: 'changed'});
|
||||
},
|
||||
|
||||
function prototypeDelete(ds) {
|
||||
|
@ -79,51 +107,42 @@ var operations = [
|
|||
},
|
||||
|
||||
function deleteAll(ds) {
|
||||
return ds.TestModel.deleteAll({ name: ds.existingInstance.name });
|
||||
return ds.TestModel.deleteAll({name: ds.existingInstance.name});
|
||||
},
|
||||
];
|
||||
/* eslint-enable camelcase */
|
||||
|
||||
var p = setupTestModels();
|
||||
let p = setupTestModels();
|
||||
operations.forEach(function(op) {
|
||||
p = p.then(runner(op));
|
||||
});
|
||||
|
||||
p.then(report, console.error);
|
||||
|
||||
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';
|
||||
|
||||
ds.connector.findOrCreate = function (model, query, data, callback) {
|
||||
this.all(model, query, {}, function (err, list) {
|
||||
if (err || (list && list[0])) return callback(err, list && list[0], false);
|
||||
this.create(model, data, {}, function (err) {
|
||||
callback(err, data, true);
|
||||
});
|
||||
}.bind(this));
|
||||
};
|
||||
|
||||
return ds;
|
||||
}
|
||||
|
||||
function createUnoptimizedDataSource() {
|
||||
var ds = new DataSource({ connector: Memory });
|
||||
const ds = new DataSource({connector: Memory});
|
||||
ds.name = 'Unoptimized';
|
||||
|
||||
// disable optimized methods
|
||||
ds.connector.updateOrCreate = false;
|
||||
ds.connector.findOrCreate = false;
|
||||
ds.connector.replaceOrCreate = false;
|
||||
|
||||
return ds;
|
||||
}
|
||||
|
||||
function setupTestModels() {
|
||||
dataSources.forEach(function setupOnDataSource(ds) {
|
||||
var TestModel = ds.TestModel = ds.createModel('TestModel', {
|
||||
id: { type: String, id: true, default: uid },
|
||||
name: { type: String, required: true },
|
||||
extra: { type: String, required: false }
|
||||
const TestModel = ds.TestModel = ds.createModel('TestModel', {
|
||||
id: {type: String, id: true, default: uid},
|
||||
name: {type: String, required: true},
|
||||
extra: {type: String, required: false},
|
||||
});
|
||||
});
|
||||
return Promise.resolve();
|
||||
|
@ -136,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);
|
||||
|
@ -144,7 +163,7 @@ function runner(fn) {
|
|||
observedContexts.push({
|
||||
operation: fn.name,
|
||||
connector: ds.name,
|
||||
hooks: {}
|
||||
hooks: {},
|
||||
});
|
||||
return fn(ds);
|
||||
});
|
||||
|
@ -154,26 +173,26 @@ function runner(fn) {
|
|||
}
|
||||
|
||||
function resetStorage(ds) {
|
||||
var TestModel = ds.TestModel;
|
||||
const TestModel = ds.TestModel;
|
||||
HOOK_NAMES.forEach(function(hook) {
|
||||
TestModel.clearObservers(hook);
|
||||
});
|
||||
return TestModel.deleteAll()
|
||||
.then(function() {
|
||||
return TestModel.create({ name: 'first' })
|
||||
return TestModel.create({name: 'first'});
|
||||
})
|
||||
.then(function(instance) {
|
||||
// Look it up from DB so that default values are retrieved
|
||||
return TestModel.findById(instance.id)
|
||||
return TestModel.findById(instance.id);
|
||||
})
|
||||
.then(function(instance) {
|
||||
ds.existingInstance = instance;
|
||||
return TestModel.create({ name: 'second' });
|
||||
return TestModel.create({name: 'second'});
|
||||
})
|
||||
.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();
|
||||
});
|
||||
|
@ -182,7 +201,7 @@ function resetStorage(ds) {
|
|||
}
|
||||
|
||||
function report() {
|
||||
console.log('<style>')
|
||||
console.log('<style>');
|
||||
console.log('td { font-family: "monospace": }');
|
||||
console.log('td, th {');
|
||||
console.log(' vertical-align: text-top;');
|
||||
|
@ -193,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;
|
||||
|
@ -207,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 +0,0 @@
|
|||
Subproject commit b41facc8505e4516161452239a25711413711373
|
|
@ -1,39 +1,46 @@
|
|||
var should = require('./init.js');
|
||||
// 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
|
||||
|
||||
var jdb = require('../');
|
||||
var DataSource = jdb.DataSource;
|
||||
'use strict';
|
||||
|
||||
var ds, Item, Variant;
|
||||
describe('Datasource-specific field types for foreign keys', function () {
|
||||
before(function () {
|
||||
const should = require('./init.js');
|
||||
|
||||
const jdb = require('../');
|
||||
const DataSource = jdb.DataSource;
|
||||
|
||||
let ds, Item, Variant;
|
||||
describe('Datasource-specific field types for foreign keys', function() {
|
||||
before(function() {
|
||||
ds = new DataSource('memory');
|
||||
Item = ds.define('Item', {
|
||||
"myProp": {
|
||||
"id": true,
|
||||
"type": "string",
|
||||
"memory": {
|
||||
"dataType": "string"
|
||||
}
|
||||
}
|
||||
myProp: {
|
||||
id: true,
|
||||
type: 'string',
|
||||
memory: {
|
||||
dataType: 'string',
|
||||
},
|
||||
},
|
||||
});
|
||||
Variant = ds.define('Variant', {}, {
|
||||
relations: {
|
||||
"item": {
|
||||
"type": "belongsTo",
|
||||
"as": "item",
|
||||
"model": "Item",
|
||||
"foreignKey": "myProp"
|
||||
}
|
||||
}
|
||||
item: {
|
||||
type: 'belongsTo',
|
||||
as: 'item',
|
||||
model: 'Item',
|
||||
foreignKey: 'myProp',
|
||||
},
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it('should create foreign key with database-specific field type', function (done) {
|
||||
var VariantDefinition = ds.getModelDefinition('Variant');
|
||||
it('should create foreign key with database-specific field type', function(done) {
|
||||
const VariantDefinition = ds.getModelDefinition('Variant');
|
||||
should.exist(VariantDefinition);
|
||||
should.exist(VariantDefinition.properties.myProp.memory);
|
||||
should.exist(VariantDefinition.properties.myProp.memory.dataType);
|
||||
VariantDefinition.properties.myProp.memory.dataType.should.be.equal("string");
|
||||
VariantDefinition.properties.myProp.memory.dataType.should.be.equal('string');
|
||||
done();
|
||||
});
|
||||
})
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue