Compare commits
235 Commits
Author | SHA1 | Date |
---|---|---|
|
13371fd2a1 | |
|
1316be55c5 | |
|
a22d4f9197 | |
|
223dd5d58e | |
|
fbd9783e47 | |
|
6f04b61f88 | |
|
ab3e159fed | |
|
5b61efb855 | |
|
e457e2651d | |
|
ab2e462ca6 | |
|
f2f7332916 | |
|
06fcaa144e | |
|
d929b57da4 | |
|
58f65454e9 | |
|
cdf48b648e | |
|
f788aae464 | |
|
6a7fc529fc | |
|
b93187b0f9 | |
|
2db09a3d00 | |
|
4edcc4f14c | |
|
b848bd9d9f | |
|
aa76a19e33 | |
|
f2f0b3aa91 | |
|
82cd6681f6 | |
|
397d141036 | |
|
aec86ccc3f | |
|
5dd9561cb6 | |
|
dd6d2a5e6c | |
|
da51c992ba | |
|
4846048e48 | |
|
edb925529e | |
|
bd276d551a | |
|
83612aac8f | |
|
af9f776ed0 | |
|
387835faef | |
|
8311138f3e | |
|
cceb8a3c26 | |
|
9b1d4885ab | |
|
58a0e6c8e9 | |
|
e682914ecd | |
|
716347e25c | |
|
cd8db0b5cf | |
|
686c6fe88e | |
|
c9a277884e | |
|
a685553b49 | |
|
4b3a3a347c | |
|
e8d115bdc2 | |
|
ae3fff9e7b | |
|
0e0ca455ac | |
|
e82a74cce3 | |
|
182ab7fb22 | |
|
3a2a645e0c | |
|
f19a8ab6fe | |
|
66ec2aa57f | |
|
f6ba1afe91 | |
|
b05d7320d7 | |
|
a175b36939 | |
|
34acd02a7e | |
|
a9a86fe380 | |
|
b83f5635cf | |
|
6caf506769 | |
|
b91938ecdf | |
|
c8a31e2725 | |
|
4226da5fc4 | |
|
1e33ec596f | |
|
c38900b785 | |
|
edb8dbc517 | |
|
b77907ffa5 | |
|
242c20fecf | |
|
2532b0b67e | |
|
0bb8c23e2d | |
|
6eb8c0ed3a | |
|
6b748748bd | |
|
da2b8d8676 | |
|
71c651123f | |
|
aad97c2036 | |
|
81d1f7406f | |
|
f1c613ac07 | |
|
a239a11656 | |
|
a4d7caba1a | |
|
818a7506d8 | |
|
69df6386fc | |
|
29c5f20d90 | |
|
be91ba3dad | |
|
b944b9d8a5 | |
|
9ea6b6ab03 | |
|
4c49b5fa1f | |
|
d66c912d06 | |
|
0488cb7ba1 | |
|
e33d10fe44 | |
|
97a55bf67a | |
|
2b7b0e1cc1 | |
|
d7a32aea70 | |
|
0bb7cd67ec | |
|
186ae2ae57 | |
|
fa644d6a31 | |
|
acc1cd0ee5 | |
|
cab1ed5463 | |
|
c9164aed17 | |
|
9f1eb419d1 | |
|
516fbbd91a | |
|
d028876b94 | |
|
78342ebf80 | |
|
8ad0f93552 | |
|
55e48cf572 | |
|
74d7043edc | |
|
3caf65d4a6 | |
|
700085c8d6 | |
|
1c30628a8a | |
|
6b0a880efa | |
|
89ff760b6d | |
|
416b86a5ec | |
|
aaa1381964 | |
|
0dac936bba | |
|
5ee731eafd | |
|
6d23c2edd1 | |
|
962efec9cd | |
|
951b61a2ae | |
|
f90e1c5e01 | |
|
cdab1519af | |
|
7e7f8facd8 | |
|
72d85fa9ea | |
|
7ad5a2e1bc | |
|
f9e9aaa8ce | |
|
cd1b31920e | |
|
9d77ba28d3 | |
|
95bcf035a8 | |
|
37e57f6943 | |
|
9c554fd4f0 | |
|
4dd195e654 | |
|
44951a1032 | |
|
0590bd1bb4 | |
|
7d93b27a80 | |
|
5a87f8df16 | |
|
f17346ba50 | |
|
995c14eccf | |
|
83fc62c0af | |
|
58090b4b80 | |
|
ec46c457d3 | |
|
85aa3fd679 | |
|
8c4c91a5d1 | |
|
cf5b78fc6c | |
|
c6d8565216 | |
|
cc4fc2197f | |
|
eb3301abf2 | |
|
36ef35dd60 | |
|
59b14093c5 | |
|
c2077dfcb0 | |
|
b2bc449e24 | |
|
3e3092c017 | |
|
07e759259e | |
|
2aead13f11 | |
|
df70f83d67 | |
|
7a148caa26 | |
|
0cd380c590 | |
|
77a35231dc | |
|
743b2d1495 | |
|
77d3d57252 | |
|
f4527c9c91 | |
|
6ddf268cb6 | |
|
c611bbbe04 | |
|
8e9fd36878 | |
|
49bdf2fe3c | |
|
66497ead70 | |
|
38f3d728b1 | |
|
0feda03d5b | |
|
386615a1df | |
|
3723f107db | |
|
d23ff84587 | |
|
50e2b49efe | |
|
0eeb99060f | |
|
6b4234b18d | |
|
2c909b8223 | |
|
c5ff5faf0d | |
|
60c9dd166b | |
|
ab791fc258 | |
|
72d48c3bfa | |
|
60750b4508 | |
|
0a4940e31e | |
|
00169d2312 | |
|
7c030c6900 | |
|
243af4bfc2 | |
|
b045e4a6be | |
|
317e00d92c | |
|
010bbc6369 | |
|
73cc950b1b | |
|
fdb453943a | |
|
3af6a1bbaa | |
|
3bf84bacde | |
|
2bfd67ccaa | |
|
b362776e73 | |
|
0bac0a933f | |
|
1babfcde9f | |
|
5dd5a674ce | |
|
1a2d8a4571 | |
|
0737f5476d | |
|
b67a096f9e | |
|
cb600d1470 | |
|
825d5a6373 | |
|
91729ee550 | |
|
010c7bcd5f | |
|
6570b94843 | |
|
3996f56ab9 | |
|
4d4070e542 | |
|
2761e62533 | |
|
0a2a45512c | |
|
4ebc517a78 | |
|
083fb5d668 | |
|
c3450df4db | |
|
f30159cd23 | |
|
64d60fb6f7 | |
|
2f02fbac89 | |
|
fb8f3d9df3 | |
|
9176ee2e11 | |
|
d0a4941668 | |
|
826ee2aca8 | |
|
8488da2e26 | |
|
33989d776c | |
|
1dd0ab31e0 | |
|
db8130ac6d | |
|
c9913927e5 | |
|
c453ad52c2 | |
|
c0a0f09f3a | |
|
fcfaf7ef53 | |
|
658d228789 | |
|
e17132d061 | |
|
ef7175a4d5 | |
|
2128ecde46 | |
|
d2d8fabb16 | |
|
4c4430ea95 | |
|
883667ce8e | |
|
0f40ca8f8e | |
|
d405432b2d | |
|
30f3161c65 | |
|
63721fd253 |
|
@ -1,37 +0,0 @@
|
|||
<!--
|
||||
Questions:
|
||||
https://groups.google.com/forum/#!forum/loopbackjs
|
||||
https://gitter.im/strongloop/loopback
|
||||
Immediate support:
|
||||
https://strongloop.com/api-connect-faqs/
|
||||
https://strongloop.com/node-js/subscription-plans/
|
||||
-->
|
||||
|
||||
# Description/Steps to reproduce
|
||||
|
||||
<!--
|
||||
If feature: A description of the feature
|
||||
If bug: Steps to reproduce
|
||||
-->
|
||||
|
||||
# Link to reproduction sandbox
|
||||
|
||||
<!--
|
||||
Link to an app sandbox for reproduction
|
||||
|
||||
Note: Failure to provide a sandbox application for reproduction purposes will result in the issue being closed.
|
||||
-->
|
||||
|
||||
# Expected result
|
||||
|
||||
<!--
|
||||
Also include actual results if bug
|
||||
-->
|
||||
|
||||
# Additional information
|
||||
|
||||
<!--
|
||||
Copy+paste the output of these two commands:
|
||||
node -e 'console.log(process.platform, process.arch, process.versions.node)'
|
||||
npm ls --prod --depth 0 | grep loopback
|
||||
-->
|
|
@ -0,0 +1,53 @@
|
|||
---
|
||||
name: Bug report
|
||||
about: Create a report to help us improve
|
||||
labels: bug
|
||||
|
||||
---
|
||||
|
||||
<!-- 🚨 STOP 🚨 STOP 🚨 STOP 🚨
|
||||
|
||||
Are you using LoopBack version 4? Please report the bug here:
|
||||
https://github.com/strongloop/loopback-next/issues/new
|
||||
|
||||
HELP US HELP YOU, PLEASE
|
||||
- Do a quick search to avoid duplicate issues
|
||||
- Provide as much information as possible (reproduction sandbox, use case for features, etc.)
|
||||
- Consider using a more suitable venue for questions such as Stack Overflow, Gitter, etc.
|
||||
|
||||
Please fill in the *entire* template below.
|
||||
|
||||
-->
|
||||
|
||||
## Steps to reproduce
|
||||
|
||||
<!-- Describe how to reproduce the issue -->
|
||||
|
||||
## Current Behavior
|
||||
|
||||
<!-- Describe the observed result -->
|
||||
|
||||
## Expected Behavior
|
||||
|
||||
<!-- Describe what did you expect instead, what is the desired outcome? -->
|
||||
|
||||
## Link to reproduction sandbox
|
||||
|
||||
<!--
|
||||
See https://loopback.io/doc/en/contrib/Reporting-issues.html#loopback-3x-bugs
|
||||
Note: Failure to provide a sandbox application for reproduction purposes will result in the issue being closed.
|
||||
-->
|
||||
|
||||
## Additional information
|
||||
|
||||
<!--
|
||||
Copy+paste the output of these two commands:
|
||||
node -e 'console.log(process.platform, process.arch, process.versions.node)'
|
||||
npm ls --prod --depth 0 | grep loopback
|
||||
-->
|
||||
|
||||
## Related Issues
|
||||
|
||||
<!-- Did you find other bugs that looked similar? -->
|
||||
|
||||
_See [Reporting Issues](http://loopback.io/doc/en/contrib/Reporting-issues.html) for more tips on writing good issues_
|
|
@ -0,0 +1,34 @@
|
|||
---
|
||||
name: Feature request
|
||||
about: Suggest an idea for this project
|
||||
labels: feature
|
||||
|
||||
---
|
||||
|
||||
<!-- 🚨 STOP 🚨 STOP 🚨 STOP 🚨
|
||||
|
||||
LoopBack version 3 is in LTS mode, we are not accepting new features.
|
||||
|
||||
We are actively developing version 4, you can find the new GitHub
|
||||
repository here: https://github.com/strongloop/loopback-next
|
||||
|
||||
-->
|
||||
|
||||
## Suggestion
|
||||
|
||||
<!-- A summary of what you'd like to see added or changed -->
|
||||
|
||||
## Use Cases
|
||||
|
||||
<!--
|
||||
What do you want to use this for?
|
||||
What shortcomings exist with current approaches?
|
||||
-->
|
||||
|
||||
## Examples
|
||||
|
||||
<!-- Show how this would be used and what the behavior would be -->
|
||||
|
||||
## Acceptance criteria
|
||||
|
||||
TBD - will be filled by the team.
|
|
@ -0,0 +1,27 @@
|
|||
---
|
||||
name: Question
|
||||
about: The issue tracker is not for questions. Please use Stack Overflow or other resources for help.
|
||||
labels: question
|
||||
|
||||
---
|
||||
|
||||
<!-- 🚨 STOP 🚨 STOP 🚨 STOP 🚨
|
||||
|
||||
THE ISSUE TRACKER IS NOT FOR QUESTIONS.
|
||||
|
||||
DO NOT CREATE A NEW ISSUE TO ASK A QUESTION.
|
||||
|
||||
Please use one of the following resources for help:
|
||||
|
||||
**Questions**
|
||||
|
||||
- https://stackoverflow.com/tags/loopbackjs
|
||||
- https://groups.google.com/forum/#!forum/loopbackjs
|
||||
- https://gitter.im/strongloop/loopback
|
||||
|
||||
**Immediate support**
|
||||
|
||||
- https://strongloop.com/api-connect-faqs/
|
||||
- https://strongloop.com/node-js/subscription-plans/
|
||||
|
||||
-->
|
|
@ -0,0 +1,16 @@
|
|||
blank_issues_enabled: false
|
||||
contact_links:
|
||||
- name: Report a security vulnerability
|
||||
url: https://loopback.io/doc/en/contrib/Reporting-issues.html#security-issues
|
||||
about: >
|
||||
LoopBack 3 has reached End-of-Life. No new security fixes will be provided
|
||||
or accepted.
|
||||
|
||||
Do not report security vulnerabilities using GitHub issues. Please send an
|
||||
email to `reachsl@us.ibm.com` instead.
|
||||
- name: Get help on StackOverflow
|
||||
url: https://stackoverflow.com/tags/loopbackjs
|
||||
about: Please ask and answer questions on StackOverflow.
|
||||
- name: Join our mailing list
|
||||
url: https://groups.google.com/forum/#!forum/loopbackjs
|
||||
about: You can also post your question to our mailing list.
|
|
@ -1,25 +1,18 @@
|
|||
### Description
|
||||
|
||||
|
||||
#### Related issues
|
||||
|
||||
<!--
|
||||
Please use the following link syntaxes:
|
||||
Please provide a high-level description of the changes made by your pull request.
|
||||
|
||||
- connect to #49 (to reference issues in the current repository)
|
||||
- connect to strongloop/loopback#49 (to reference issues in another repository)
|
||||
Include references to all related GitHub issues and other pull requests, for example:
|
||||
|
||||
Fixes #123
|
||||
Implements #254
|
||||
See also #23
|
||||
-->
|
||||
|
||||
- connect to <link_to_referenced_issue>
|
||||
## Checklist
|
||||
|
||||
### Checklist
|
||||
|
||||
<!--
|
||||
- Please mark your choice with an "x" (i.e. [x], see
|
||||
https://github.com/blog/1375-task-lists-in-gfm-issues-pulls-comments)
|
||||
- PR's without test coverage will be closed.
|
||||
-->
|
||||
👉 [Read and sign the CLA (Contributor License Agreement)](https://cla.strongloop.com/agreements/strongloop/loopback) 👈
|
||||
|
||||
- [ ] `npm test` passes on your machine
|
||||
- [ ] New tests added or existing tests modified to cover all changes
|
||||
- [ ] Code conforms with the [style
|
||||
guide](http://loopback.io/doc/en/contrib/style-guide.html)
|
||||
- [ ] Code conforms with the [style guide](https://loopback.io/doc/en/contrib/style-guide-es6.html)
|
||||
- [ ] Commit messages are following our [guidelines](https://loopback.io/doc/en/contrib/git-commit-messages.html)
|
||||
|
|
|
@ -7,8 +7,6 @@ exemptLabels:
|
|||
- pinned
|
||||
- security
|
||||
- critical
|
||||
- p1
|
||||
- major
|
||||
# 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
|
||||
|
@ -20,4 +18,4 @@ markComment: >
|
|||
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.
|
||||
listed in the [`CODEOWNERS`](https://github.com/strongloop/loopback/blob/master/CODEOWNERS) file at the top-level of this repository.
|
||||
|
|
19
.travis.yml
19
.travis.yml
|
@ -1,22 +1,15 @@
|
|||
sudo: false
|
||||
language: node_js
|
||||
node_js:
|
||||
- "4"
|
||||
- "6"
|
||||
- "8"
|
||||
- "10"
|
||||
- "12"
|
||||
- "14"
|
||||
|
||||
addons:
|
||||
chrome: stable
|
||||
|
||||
after_success: npm run coverage
|
||||
|
||||
# see https://www.npmjs.com/package/phantomjs-prebuilt#continuous-integration
|
||||
cache:
|
||||
directories:
|
||||
- travis_phantomjs
|
||||
before_install:
|
||||
- npm config set registry http://ci.strongloop.com:4873/
|
||||
# Upgrade PhantomJS to v2.1.1.
|
||||
- "export PHANTOMJS_VERSION=2.1.1"
|
||||
- "export PATH=$PWD/travis_phantomjs/phantomjs-$PHANTOMJS_VERSION-linux-x86_64/bin:$PATH"
|
||||
- "if [ $(phantomjs --version) != $PHANTOMJS_VERSION ]; then rm -rf $PWD/travis_phantomjs; mkdir -p $PWD/travis_phantomjs; fi"
|
||||
- "if [ $(phantomjs --version) != $PHANTOMJS_VERSION ]; then wget https://github.com/Medium/phantomjs/releases/download/v$PHANTOMJS_VERSION/phantomjs-$PHANTOMJS_VERSION-linux-x86_64.tar.bz2 -O $PWD/travis_phantomjs/phantomjs-$PHANTOMJS_VERSION-linux-x86_64.tar.bz2; fi"
|
||||
- "if [ $(phantomjs --version) != $PHANTOMJS_VERSION ]; then tar -xvf $PWD/travis_phantomjs/phantomjs-$PHANTOMJS_VERSION-linux-x86_64.tar.bz2 -C $PWD/travis_phantomjs; fi"
|
||||
- "phantomjs --version"
|
||||
|
|
362
CHANGES.md
362
CHANGES.md
|
@ -1,3 +1,365 @@
|
|||
2020-11-25, Version 3.28.0
|
||||
==========================
|
||||
|
||||
* upgrade nodemailer to greater than 6.4.16 (jannyHou)
|
||||
|
||||
* chore: sync LoopBack 4 with Node.js v14 EOL (Rifa Achrinza)
|
||||
|
||||
* chore: add Node.js 14 to travis (Diana Lau)
|
||||
|
||||
* Remove "major" and "p1" from stalebot (Miroslav Bajtoš)
|
||||
|
||||
|
||||
2020-03-06, Version 3.27.0
|
||||
==========================
|
||||
|
||||
* Update LTS status in README (Miroslav Bajtoš)
|
||||
|
||||
* chore: update copyright year (Diana Lau)
|
||||
|
||||
* feat: change hasone relation error message (Sujesh T)
|
||||
|
||||
* chore: disable security issue reporting (Nora)
|
||||
|
||||
* chore: fix eslint violations (Nora)
|
||||
|
||||
* fixup! manual fixes (Miroslav Bajtoš)
|
||||
|
||||
* fixup! eslint --fix . (Miroslav Bajtoš)
|
||||
|
||||
* chore: update eslint & eslint-config to latest (Miroslav Bajtoš)
|
||||
|
||||
* chore: update dev-dependencies (Miroslav Bajtoš)
|
||||
|
||||
* chore: update chai to v4, dirty-chai to v2 (Miroslav Bajtoš)
|
||||
|
||||
* Updated "ismail" package to v3.2 (Stanislav Sarbinski)
|
||||
|
||||
* Introduce issue templates for bugs, features, etc. (Miroslav Bajtoš)
|
||||
|
||||
* Improve PULL_REQUEST_TEMPLATE (Miroslav Bajtoš)
|
||||
|
||||
* test: use Chromium (not Chrome) when available (Miroslav Bajtoš)
|
||||
|
||||
* test: disable Chrome sandboxing when inside Docker (Miroslav Bajtoš)
|
||||
|
||||
* test: switch from PhantomJS to HeadlessChrome (Miroslav Bajtoš)
|
||||
|
||||
|
||||
2019-05-31, Version 3.26.0
|
||||
==========================
|
||||
|
||||
* fix: disallow queries in username and email fields (Hage Yaapa)
|
||||
|
||||
* Ignore failing downstream dependencies (Miroslav Bajtoš)
|
||||
|
||||
* Upgrade nyc to version 14 (Miroslav Bajtoš)
|
||||
|
||||
* Update Karma dependencies to latest versions (Miroslav Bajtoš)
|
||||
|
||||
* Drop Node.js 6.x from the supported versions (Miroslav Bajtoš)
|
||||
|
||||
* Fix Model.exists() to work with remote connector (Maxim Sharai)
|
||||
|
||||
* chore: update copyrights years (Agnes Lin)
|
||||
|
||||
* Update LTS status (Diana Lau)
|
||||
|
||||
* Enable Node.js 12.x on Travis CI (Miroslav Bajtoš)
|
||||
|
||||
* chore: update copyright year (Diana Lau)
|
||||
|
||||
* chore: update LB3 EOL date (Diana Lau)
|
||||
|
||||
|
||||
2019-03-15, Version 3.25.1
|
||||
==========================
|
||||
|
||||
* Back-ticks added to highlight example JSON (Quentin Presley)
|
||||
|
||||
* Add same change to description for findOne (Quentin Presley)
|
||||
|
||||
* Update the description for persisted-models (Quentin Presley)
|
||||
|
||||
* handle $2b$ in hashed password check (Sylvain Dumont)
|
||||
|
||||
|
||||
2019-02-05, Version 3.25.0
|
||||
==========================
|
||||
|
||||
* Support middleware injected by AppDynamics. (Mike Li)
|
||||
|
||||
|
||||
2019-01-11, Version 3.24.2
|
||||
==========================
|
||||
|
||||
* Fix crash when modifying an unknown user (Matheus Horstmann)
|
||||
|
||||
|
||||
2019-01-08, Version 3.24.1
|
||||
==========================
|
||||
|
||||
* Update underscore.string to 3.3.5 (Francois)
|
||||
|
||||
* Fix: treat empty access token string as undefined (andrey-abramow)
|
||||
|
||||
|
||||
2018-11-15, Version 3.24.0
|
||||
==========================
|
||||
|
||||
* Set juggler options for remote calls (Raymond Feng)
|
||||
|
||||
* Speed up ACL tests by reducing saltWorkFactor (Miroslav Bajtoš)
|
||||
|
||||
|
||||
2018-10-25, Version 3.23.2
|
||||
==========================
|
||||
|
||||
* Fix ACL check to support model wildcard (Moshe Malka)
|
||||
|
||||
|
||||
2018-10-18, Version 3.23.1
|
||||
==========================
|
||||
|
||||
* README: highlight Active LTS at the top (Miroslav Bajtoš)
|
||||
|
||||
|
||||
2018-10-09, Version 3.23.0
|
||||
==========================
|
||||
|
||||
* Clear handler cache when a method is added/removed (Mohammed Essehemy)
|
||||
|
||||
* Add `options.preserveAccessTokens` (lchaglla)
|
||||
|
||||
* Update LB3 to be active LTS (Diana Lau)
|
||||
|
||||
* Fix ACL tests to wait until all assertions finish (Moshe Malka)
|
||||
|
||||
* chore: update to latest linting rules (virkt25)
|
||||
|
||||
|
||||
2018-09-12, Version 3.22.3
|
||||
==========================
|
||||
|
||||
* chore: use grunt to install optional phantomjs (virkt25)
|
||||
|
||||
* [WebFM] fr translation (candytangnb)
|
||||
|
||||
|
||||
2018-08-29, Version 3.22.2
|
||||
==========================
|
||||
|
||||
* [WebFM] tr translation (candytangnb)
|
||||
|
||||
* [WebFM] de translation (candytangnb)
|
||||
|
||||
* [WebFM] cs/es/fr/it/nl/pl/pt_BR/ru translation (candytangnb)
|
||||
|
||||
|
||||
2018-08-22, Version 3.22.1
|
||||
==========================
|
||||
|
||||
* [WebFM] ja/ko/zh_CN/zh_TW translation (candytangnb)
|
||||
|
||||
* remove unnecessary format call (Diana Lau)
|
||||
|
||||
* Make desc when export-api-def translatable (Diana Lau)
|
||||
|
||||
|
||||
2018-08-08, Version 3.22.0
|
||||
==========================
|
||||
|
||||
* fix: accessToken create default acl (virkt25)
|
||||
|
||||
* add: ppc64 and s390x to not run UI tests (Thomas Leah)
|
||||
|
||||
* chore: update deps + fix linting + .npmrc (virkt25)
|
||||
|
||||
* Update Loopback 2.x EOL dates (Chris Bailey)
|
||||
|
||||
* Fix formatting (Chris Bailey)
|
||||
|
||||
* Update support badge and move LTS section (Chris Bailey)
|
||||
|
||||
* Add badges and information for LTS and support (Chris Bailey)
|
||||
|
||||
|
||||
2018-07-09, Version 3.21.0
|
||||
==========================
|
||||
|
||||
* Make verifyUserRelations() more robust (mcitdev)
|
||||
|
||||
* Fix crash in verifyUserRelations (ryanxwelch)
|
||||
|
||||
* Fix crash in User model's "before delete" hook (mcitdev)
|
||||
|
||||
* [WebFM] cs/pl/ru translation (candytangnb)
|
||||
|
||||
* Update strong-error-handler (shimks)
|
||||
|
||||
|
||||
2018-06-12, Version 3.20.0
|
||||
==========================
|
||||
|
||||
* Update strong-globalize to 4.x (Miroslav Bajtoš)
|
||||
|
||||
* Update nodemailer to v4.x (Dimitris)
|
||||
|
||||
* Drop support for Node.js 4.x (Miroslav Bajtoš)
|
||||
|
||||
|
||||
2018-06-04, Version 3.19.3
|
||||
==========================
|
||||
|
||||
* Provide link to CODEOWNERS (Aditya Agarwal)
|
||||
|
||||
* fix bug in User.verify when confirm is disabled (wolrajhti)
|
||||
|
||||
* Enable Node.js 10.x on Travis CI (Miroslav Bajtoš)
|
||||
|
||||
|
||||
2018-05-29, Version 3.19.2
|
||||
==========================
|
||||
|
||||
* Add check for undefined user email in setter (Kevin Scroggins)
|
||||
|
||||
|
||||
2018-05-21, Version 3.19.1
|
||||
==========================
|
||||
|
||||
* Fix isOwner() bug in multiple-principal setup (Miroslav Bajtoš)
|
||||
|
||||
|
||||
2018-04-17, Version 3.19.0
|
||||
==========================
|
||||
|
||||
* feat: remove all references to a Model (Miroslav Bajtoš)
|
||||
|
||||
|
||||
2018-03-22, Version 3.18.3
|
||||
==========================
|
||||
|
||||
* Remove forgotten debugger statement (Miroslav Bajtoš)
|
||||
|
||||
* Fix role check in apps with multiple user models (Miroslav Bajtoš)
|
||||
|
||||
* Fix formatting issues reported by recent eslint (Miroslav Bajtoš)
|
||||
|
||||
* CODEOWNERS: add nitro404 (Miroslav Bajtoš)
|
||||
|
||||
* test: add missing "return" in a promise-style test (Miroslav Bajtoš)
|
||||
|
||||
|
||||
2018-02-08, Version 3.18.2
|
||||
==========================
|
||||
|
||||
* model: fix infinite loop on nestRemoting (Kevin Delisle)
|
||||
|
||||
* Use statusCode prop for user errors (Zak Barbuto)
|
||||
|
||||
|
||||
2018-01-31, Version 3.18.1
|
||||
==========================
|
||||
|
||||
* update: juggler to version including security fix. (Taranveer Virk)
|
||||
|
||||
|
||||
2018-01-29, Version 3.18.0
|
||||
==========================
|
||||
|
||||
* fix: preserve datasource name (Kevin Scroggins)
|
||||
|
||||
* Update Copyright Years (Justin Ross)
|
||||
|
||||
* Support options.filter in createChangeStream (Edward Choh)
|
||||
|
||||
* fixup! add top-level dep on eslint-plugin-mocha (Miroslav Bajtoš)
|
||||
|
||||
* Update eslint and eslint-config to latest (Miroslav Bajtoš)
|
||||
|
||||
|
||||
2017-12-12, Version 3.17.1
|
||||
==========================
|
||||
|
||||
* Update nestRemoting to pass optionsFromContext (bmatson)
|
||||
|
||||
* fix(test): rem exclusive test (Samuel Reed)
|
||||
|
||||
* fix(test): working test with 0 userId (Samuel Reed)
|
||||
|
||||
* fix(AccessContext): Tighten userid/appid checks (Samuel Reed)
|
||||
|
||||
* fix(id): replace with != null (Samuel Reed)
|
||||
|
||||
|
||||
2017-11-29, Version 3.17.0
|
||||
==========================
|
||||
|
||||
* Added missing DateString type in loopback index (CSLTech)
|
||||
|
||||
* chore:update license (Diana Lau)
|
||||
|
||||
|
||||
2017-10-30, Version 3.16.2
|
||||
==========================
|
||||
|
||||
* Fix "POST /change-password" for multi-user setup (Miroslav Bajtoš)
|
||||
|
||||
|
||||
2017-10-27, Version 3.16.1
|
||||
==========================
|
||||
|
||||
* Fix createOnlyInstance for related methods (Raymond Feng)
|
||||
|
||||
|
||||
2017-10-24, Version 3.16.0
|
||||
==========================
|
||||
|
||||
* Fix "POST /reset-password" for multi-user setup (Miroslav Bajtoš)
|
||||
|
||||
* test: extract helpers for logging HTTP errors (Miroslav Bajtoš)
|
||||
|
||||
* CODEOWNERS: move @lehni to Alumni section (Miroslav Bajtoš)
|
||||
|
||||
|
||||
2017-10-13, Version 3.15.0
|
||||
==========================
|
||||
|
||||
* update strong-globalize to 3.1.0 (shimks)
|
||||
|
||||
* Fix handling of user verification options (Miroslav Bajtoš)
|
||||
|
||||
* Handle missing getUpdateOnlyProperties fn (Jürg Lehni)
|
||||
|
||||
* test: fix too strict test assertion (Miroslav Bajtoš)
|
||||
|
||||
* Fix typo (Siegfried Ehret)
|
||||
|
||||
|
||||
2017-09-28, Version 3.14.0
|
||||
==========================
|
||||
|
||||
* Allow declarative nestRemoting for relations (Raymond Feng)
|
||||
|
||||
|
||||
2017-09-27, Version 3.13.0
|
||||
==========================
|
||||
|
||||
* Fix OWNER role to handle multiple relations (pierreclr)
|
||||
|
||||
* Fix acl.resolvePermission for wildcard req (Farid Neshat)
|
||||
|
||||
* CODEOWNERS: add zbarbuto (Miroslav Bajtoš)
|
||||
|
||||
|
||||
2017-09-25, Version 3.12.0
|
||||
==========================
|
||||
|
||||
* Fix relation race condition in model glob (Zak Barbuto)
|
||||
|
||||
* CODEOWNERS: add lehni (Miroslav Bajtoš)
|
||||
|
||||
|
||||
2017-08-23, Version 3.11.1
|
||||
==========================
|
||||
|
||||
|
|
|
@ -2,4 +2,10 @@
|
|||
# Each line is a file pattern followed by one or more owners,
|
||||
# the last matching pattern has the most precendence.
|
||||
|
||||
* @bajtos @fabien @clarkorz @ebarault
|
||||
# Current maintainers
|
||||
|
||||
* @bajtos @fabien @clarkorz @ebarault @zbarbuto @nitro404
|
||||
|
||||
# Alumni
|
||||
|
||||
_ @lehni
|
||||
|
|
20
Gruntfile.js
20
Gruntfile.js
|
@ -1,10 +1,10 @@
|
|||
// Copyright IBM Corp. 2014,2016. All Rights Reserved.
|
||||
// Copyright IBM Corp. 2014,2019. All Rights Reserved.
|
||||
// Node module: loopback
|
||||
// This file is licensed under the MIT License.
|
||||
// License text available at https://opensource.org/licenses/MIT
|
||||
|
||||
/* global module:false */
|
||||
'use strict';
|
||||
|
||||
module.exports = function(grunt) {
|
||||
// Do not report warnings from unit-tests exercising deprecated paths
|
||||
process.env.NO_DEPRECATION = 'loopback';
|
||||
|
@ -104,7 +104,7 @@ module.exports = function(grunt) {
|
|||
karma: {
|
||||
'unit-once': {
|
||||
configFile: 'test/karma.conf.js',
|
||||
browsers: ['PhantomJS'],
|
||||
browsers: ['ChromeDocker'],
|
||||
singleRun: true,
|
||||
reporters: ['dots', 'junit'],
|
||||
|
||||
|
@ -218,16 +218,17 @@ module.exports = function(grunt) {
|
|||
grunt.loadNpmTasks('grunt-karma');
|
||||
|
||||
grunt.registerTask('e2e-server', function() {
|
||||
var done = this.async();
|
||||
var app = require('./test/fixtures/e2e/app');
|
||||
const done = this.async();
|
||||
const app = require('./test/fixtures/e2e/app');
|
||||
app.listen(0, function() {
|
||||
process.env.PORT = this.address().port;
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
grunt.registerTask('skip-karma-on-windows', function() {
|
||||
console.log('*** SKIPPING PHANTOM-JS BASED TESTS ON WINDOWS ***');
|
||||
grunt.registerTask('skip-karma', function() {
|
||||
console.log(`*** SKIPPING PHANTOM-JS BASED TESTS ON ${process.platform}` +
|
||||
` ${process.arch} ***`);
|
||||
});
|
||||
|
||||
grunt.registerTask('e2e', ['e2e-server', 'karma:e2e']);
|
||||
|
@ -238,8 +239,9 @@ module.exports = function(grunt) {
|
|||
grunt.registerTask('test', [
|
||||
'eslint',
|
||||
process.env.JENKINS_HOME ? 'mochaTest:unit-xml' : 'mochaTest:unit',
|
||||
process.env.JENKINS_HOME && /^win/.test(process.platform) ?
|
||||
'skip-karma-on-windows' : 'karma:unit-once',
|
||||
process.env.JENKINS_HOME && (/^win/.test(process.platform) ||
|
||||
/^s390x/.test(process.arch) || /^ppc64/.test(process.arch)) ?
|
||||
'skip-karma' : 'karma:unit-once',
|
||||
]);
|
||||
|
||||
// alias for sl-ci-run and `npm test`
|
||||
|
|
2
LICENSE
2
LICENSE
|
@ -1,4 +1,4 @@
|
|||
Copyright (c) IBM Corp. 2013,2016. All Rights Reserved.
|
||||
Copyright (c) IBM Corp. 2013,2018. All Rights Reserved.
|
||||
Node module: loopback
|
||||
This project is licensed under the MIT License, full text below.
|
||||
|
||||
|
|
33
README.md
33
README.md
|
@ -1,6 +1,24 @@
|
|||
# LoopBack
|
||||
|
||||
[](https://gitter.im/strongloop/loopback?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
|
||||
[](http://github.com/CloudNativeJS/ModuleLTS)
|
||||
[](http://ibm.biz/node-support)
|
||||
|
||||
**⚠️ LoopBack 3 has reached end of life. We are no longer accepting pull requests or providing
|
||||
support for community users. The only exception is fixes for critical bugs and security
|
||||
vulnerabilities provided as part of support for IBM API Connect customers.
|
||||
We urge all LoopBack 3 users to migrate their applications to LoopBack 4 as soon as possible.
|
||||
Learn more about
|
||||
<a href="https://loopback.io/doc/en/contrib/Long-term-support.html">LoopBack's long term support policy.</a>
|
||||
will be provided or accepted. (See
|
||||
[Module Long Term Support Policy](#module-long-term-support-policy) below.)**
|
||||
|
||||
We urge all LoopBack 3 users to migrate their applications to LoopBack 4 as
|
||||
soon as possible. Refer to our
|
||||
[Migration Guide](https://loopback.io/doc/en/lb4/migration-overview.html)
|
||||
for more information on how to upgrade.
|
||||
|
||||
## Overview
|
||||
|
||||
LoopBack is a highly-extensible, open-source Node.js framework that enables you to:
|
||||
|
||||
|
@ -22,11 +40,18 @@ LoopBack tools include:
|
|||
|
||||
For more details, see [https://loopback.io/](https://loopback.io/).
|
||||
|
||||
## Supported versions
|
||||
|
||||
Current|Long Term Support
|
||||
:-:|:-:
|
||||
3.x|2.x
|
||||
## Module Long Term Support Policy
|
||||
|
||||
LoopBack 3.x has reached End-of-Life.
|
||||
|
||||
This module adopts the [Module Long Term Support (LTS)](http://github.com/CloudNativeJS/ModuleLTS) policy, with the following End Of Life (EOL) dates:
|
||||
|
||||
| Version | Status | Published | EOL |
|
||||
| ---------- | --------------- | --------- | -------------------- |
|
||||
| LoopBack 4 | Current | Oct 2018 | Apr 2023 _(minimum)_ |
|
||||
| LoopBack 3 | End-of-Life | Dec 2016 | Dec 2020 |
|
||||
| LoopBack 2 | End-of-Life | Jul 2014 | Apr 2019 |
|
||||
|
||||
Learn more about our LTS plan in [docs](https://loopback.io/doc/en/contrib/Long-term-support.html).
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// Copyright IBM Corp. 2014,2016. All Rights Reserved.
|
||||
// Copyright IBM Corp. 2014,2019. All Rights Reserved.
|
||||
// Node module: loopback
|
||||
// This file is licensed under the MIT License.
|
||||
// License text available at https://opensource.org/licenses/MIT
|
||||
|
@ -8,11 +8,11 @@
|
|||
*/
|
||||
|
||||
'use strict';
|
||||
var g = require('../../lib/globalize');
|
||||
var loopback = require('../../lib/loopback');
|
||||
var assert = require('assert');
|
||||
var uid = require('uid2');
|
||||
var DEFAULT_TOKEN_LEN = 64;
|
||||
const g = require('../../lib/globalize');
|
||||
const loopback = require('../../lib/loopback');
|
||||
const assert = require('assert');
|
||||
const uid = require('uid2');
|
||||
const DEFAULT_TOKEN_LEN = 64;
|
||||
|
||||
/**
|
||||
* Token based authentication and access control.
|
||||
|
@ -93,11 +93,11 @@ module.exports = function(AccessToken) {
|
|||
*/
|
||||
AccessToken.getIdForRequest = function(req, options) {
|
||||
options = options || {};
|
||||
var params = options.params || [];
|
||||
var headers = options.headers || [];
|
||||
var cookies = options.cookies || [];
|
||||
var i = 0;
|
||||
var length, id;
|
||||
let params = options.params || [];
|
||||
let headers = options.headers || [];
|
||||
let cookies = options.cookies || [];
|
||||
let i = 0;
|
||||
let length, id;
|
||||
|
||||
// https://github.com/strongloop/loopback/issues/1326
|
||||
if (options.searchDefaultTokenKeys !== false) {
|
||||
|
@ -107,12 +107,12 @@ module.exports = function(AccessToken) {
|
|||
}
|
||||
|
||||
for (length = params.length; i < length; i++) {
|
||||
var param = params[i];
|
||||
const param = params[i];
|
||||
// replacement for deprecated req.param()
|
||||
id = req.params && req.params[param] !== undefined ? req.params[param] :
|
||||
req.body && req.body[param] !== undefined ? req.body[param] :
|
||||
req.query && req.query[param] !== undefined ? req.query[param] :
|
||||
undefined;
|
||||
req.query && req.query[param] !== undefined ? req.query[param] :
|
||||
undefined;
|
||||
|
||||
if (typeof id === 'string') {
|
||||
return id;
|
||||
|
@ -125,11 +125,16 @@ module.exports = function(AccessToken) {
|
|||
if (typeof id === 'string') {
|
||||
// Add support for oAuth 2.0 bearer token
|
||||
// http://tools.ietf.org/html/rfc6750
|
||||
|
||||
// To prevent Error: Model::findById requires the id argument
|
||||
// with loopback-datasource-juggler 2.56.0+
|
||||
if (id === '') continue;
|
||||
|
||||
if (id.indexOf('Bearer ') === 0) {
|
||||
id = id.substring(7);
|
||||
if (options.bearerTokenBase64Encoded) {
|
||||
// Decode from base64
|
||||
var buf = new Buffer(id, 'base64');
|
||||
const buf = new Buffer(id, 'base64');
|
||||
id = buf.toString('utf8');
|
||||
}
|
||||
} else if (/^Basic /i.test(id)) {
|
||||
|
@ -142,7 +147,7 @@ module.exports = function(AccessToken) {
|
|||
// "a2b2c3:" (curl http://a2b2c3@localhost:3000/)
|
||||
// "token:a2b2c3" (curl http://token:a2b2c3@localhost:3000/)
|
||||
// ":a2b2c3"
|
||||
var parts = /^([^:]*):(.*)$/.exec(id);
|
||||
const parts = /^([^:]*):(.*)$/.exec(id);
|
||||
if (parts) {
|
||||
id = parts[2].length > parts[1].length ? parts[2] : parts[1];
|
||||
}
|
||||
|
@ -181,7 +186,7 @@ module.exports = function(AccessToken) {
|
|||
} else if (isValid) {
|
||||
cb(null, token);
|
||||
} else {
|
||||
var e = new Error(g.f('Invalid Access Token'));
|
||||
const e = new Error(g.f('Invalid Access Token'));
|
||||
e.status = e.statusCode = 401;
|
||||
e.code = 'INVALID_TOKEN';
|
||||
cb(e);
|
||||
|
@ -208,7 +213,7 @@ module.exports = function(AccessToken) {
|
|||
options = {};
|
||||
}
|
||||
|
||||
var id = this.getIdForRequest(req, options);
|
||||
const id = this.getIdForRequest(req, options);
|
||||
|
||||
if (id) {
|
||||
this.resolve(id, cb);
|
||||
|
@ -227,16 +232,16 @@ module.exports = function(AccessToken) {
|
|||
AccessToken.prototype.validate = function(cb) {
|
||||
try {
|
||||
assert(
|
||||
this.created && typeof this.created.getTime === 'function',
|
||||
'token.created must be a valid Date'
|
||||
this.created && typeof this.created.getTime === 'function',
|
||||
'token.created must be a valid Date',
|
||||
);
|
||||
assert(this.ttl !== 0, 'token.ttl must be not be 0');
|
||||
assert(this.ttl, 'token.ttl must exist');
|
||||
assert(this.ttl >= -1, 'token.ttl must be >= -1');
|
||||
|
||||
var AccessToken = this.constructor;
|
||||
var userRelation = AccessToken.relations.user; // may not be set up
|
||||
var User = userRelation && userRelation.modelTo;
|
||||
const AccessToken = this.constructor;
|
||||
const userRelation = AccessToken.relations.user; // may not be set up
|
||||
let User = userRelation && userRelation.modelTo;
|
||||
|
||||
// redefine user model if accessToken's principalType is available
|
||||
if (this.principalType) {
|
||||
|
@ -248,13 +253,13 @@ module.exports = function(AccessToken) {
|
|||
}
|
||||
}
|
||||
|
||||
var now = Date.now();
|
||||
var created = this.created.getTime();
|
||||
var elapsedSeconds = (now - created) / 1000;
|
||||
var secondsToLive = this.ttl;
|
||||
var eternalTokensAllowed = !!(User && User.settings.allowEternalTokens);
|
||||
var isEternalToken = secondsToLive === -1;
|
||||
var isValid = isEternalToken ?
|
||||
const now = Date.now();
|
||||
const created = this.created.getTime();
|
||||
const elapsedSeconds = (now - created) / 1000;
|
||||
const secondsToLive = this.ttl;
|
||||
const eternalTokensAllowed = !!(User && User.settings.allowEternalTokens);
|
||||
const isEternalToken = secondsToLive === -1;
|
||||
const isValid = isEternalToken ?
|
||||
eternalTokensAllowed :
|
||||
elapsedSeconds < secondsToLive;
|
||||
|
||||
|
|
|
@ -32,12 +32,6 @@
|
|||
"principalType": "ROLE",
|
||||
"principalId": "$everyone",
|
||||
"permission": "DENY"
|
||||
},
|
||||
{
|
||||
"principalType": "ROLE",
|
||||
"principalId": "$everyone",
|
||||
"property": "create",
|
||||
"permission": "ALLOW"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// Copyright IBM Corp. 2014,2016. All Rights Reserved.
|
||||
// Copyright IBM Corp. 2014,2019. All Rights Reserved.
|
||||
// Node module: loopback
|
||||
// This file is licensed under the MIT License.
|
||||
// License text available at https://opensource.org/licenses/MIT
|
||||
|
@ -30,20 +30,20 @@
|
|||
Map to oAuth 2.0 scopes
|
||||
*/
|
||||
|
||||
var g = require('../../lib/globalize');
|
||||
var loopback = require('../../lib/loopback');
|
||||
var utils = require('../../lib/utils');
|
||||
var async = require('async');
|
||||
var extend = require('util')._extend;
|
||||
var assert = require('assert');
|
||||
var debug = require('debug')('loopback:security:acl');
|
||||
const g = require('../../lib/globalize');
|
||||
const loopback = require('../../lib/loopback');
|
||||
const utils = require('../../lib/utils');
|
||||
const async = require('async');
|
||||
const extend = require('util')._extend;
|
||||
const assert = require('assert');
|
||||
const debug = require('debug')('loopback:security:acl');
|
||||
|
||||
var ctx = require('../../lib/access-context');
|
||||
var AccessContext = ctx.AccessContext;
|
||||
var Principal = ctx.Principal;
|
||||
var AccessRequest = ctx.AccessRequest;
|
||||
const ctx = require('../../lib/access-context');
|
||||
const AccessContext = ctx.AccessContext;
|
||||
const Principal = ctx.Principal;
|
||||
const AccessRequest = ctx.AccessRequest;
|
||||
|
||||
var Role = loopback.Role;
|
||||
const Role = loopback.Role;
|
||||
assert(Role, 'Role model must be defined before ACL model');
|
||||
|
||||
/**
|
||||
|
@ -107,18 +107,18 @@ module.exports = function(ACL) {
|
|||
* @returns {Number}
|
||||
*/
|
||||
ACL.getMatchingScore = function getMatchingScore(rule, req) {
|
||||
var props = ['model', 'property', 'accessType'];
|
||||
var score = 0;
|
||||
const props = ['model', 'property', 'accessType'];
|
||||
let score = 0;
|
||||
|
||||
for (var i = 0; i < props.length; i++) {
|
||||
for (let i = 0; i < props.length; i++) {
|
||||
// Shift the score by 4 for each of the properties as the weight
|
||||
score = score * 4;
|
||||
var ruleValue = rule[props[i]] || ACL.ALL;
|
||||
var requestedValue = req[props[i]] || ACL.ALL;
|
||||
var isMatchingMethodName = props[i] === 'property' &&
|
||||
const ruleValue = rule[props[i]] || ACL.ALL;
|
||||
const requestedValue = req[props[i]] || ACL.ALL;
|
||||
const isMatchingMethodName = props[i] === 'property' &&
|
||||
req.methodNames.indexOf(ruleValue) !== -1;
|
||||
|
||||
var isMatchingAccessType = ruleValue === requestedValue;
|
||||
let isMatchingAccessType = ruleValue === requestedValue;
|
||||
if (props[i] === 'accessType' && !isMatchingAccessType) {
|
||||
switch (ruleValue) {
|
||||
case ACL.EXECUTE:
|
||||
|
@ -219,11 +219,11 @@ module.exports = function(ACL) {
|
|||
acls = acls.sort(function(rule1, rule2) {
|
||||
return ACL.getMatchingScore(rule2, req) - ACL.getMatchingScore(rule1, req);
|
||||
});
|
||||
var permission = ACL.DEFAULT;
|
||||
var score = 0;
|
||||
let permission = ACL.DEFAULT;
|
||||
let score = 0;
|
||||
|
||||
for (var i = 0; i < acls.length; i++) {
|
||||
var candidate = acls[i];
|
||||
for (let i = 0; i < acls.length; i++) {
|
||||
const candidate = acls[i];
|
||||
score = ACL.getMatchingScore(candidate, req);
|
||||
if (score < 0) {
|
||||
// the highest scored ACL did not match
|
||||
|
@ -239,10 +239,11 @@ module.exports = function(ACL) {
|
|||
break;
|
||||
}
|
||||
// For wildcard match, find the strongest permission
|
||||
var candidateOrder = AccessContext.permissionOrder[candidate.permission];
|
||||
var permissionOrder = AccessContext.permissionOrder[permission];
|
||||
const candidateOrder = AccessContext.permissionOrder[candidate.permission];
|
||||
const permissionOrder = AccessContext.permissionOrder[permission];
|
||||
if (candidateOrder > permissionOrder) {
|
||||
permission = candidate.permission;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -254,7 +255,7 @@ module.exports = function(ACL) {
|
|||
debug('with score:', acl.score(req));
|
||||
});
|
||||
}
|
||||
var res = new AccessRequest({
|
||||
const res = new AccessRequest({
|
||||
model: req.model,
|
||||
property: req.property,
|
||||
accessType: req.accessType,
|
||||
|
@ -275,11 +276,11 @@ module.exports = function(ACL) {
|
|||
* @return {Object[]} An array of ACLs
|
||||
*/
|
||||
ACL.getStaticACLs = function getStaticACLs(model, property) {
|
||||
var modelClass = this.registry.findModel(model);
|
||||
var staticACLs = [];
|
||||
const modelClass = this.registry.findModel(model);
|
||||
const staticACLs = [];
|
||||
if (modelClass && modelClass.settings.acls) {
|
||||
modelClass.settings.acls.forEach(function(acl) {
|
||||
var prop = acl.property;
|
||||
let prop = acl.property;
|
||||
// We support static ACL property with array of string values.
|
||||
if (Array.isArray(prop) && prop.indexOf(property) >= 0)
|
||||
prop = property;
|
||||
|
@ -295,7 +296,7 @@ module.exports = function(ACL) {
|
|||
}
|
||||
});
|
||||
}
|
||||
var prop = modelClass && (
|
||||
const prop = modelClass && (
|
||||
// regular property
|
||||
modelClass.definition.properties[property] ||
|
||||
// relation/scope
|
||||
|
@ -331,24 +332,24 @@ module.exports = function(ACL) {
|
|||
* @param {AccessRequest} result The resolved access request.
|
||||
*/
|
||||
ACL.checkPermission = function checkPermission(principalType, principalId,
|
||||
model, property, accessType,
|
||||
callback) {
|
||||
model, property, accessType,
|
||||
callback) {
|
||||
if (!callback) callback = utils.createPromiseCallback();
|
||||
if (principalId !== null && principalId !== undefined && (typeof principalId !== 'string')) {
|
||||
principalId = principalId.toString();
|
||||
}
|
||||
property = property || ACL.ALL;
|
||||
var propertyQuery = (property === ACL.ALL) ? undefined : {inq: [property, ACL.ALL]};
|
||||
const propertyQuery = (property === ACL.ALL) ? undefined : {inq: [property, ACL.ALL]};
|
||||
accessType = accessType || ACL.ALL;
|
||||
var accessTypeQuery = (accessType === ACL.ALL) ? undefined :
|
||||
const accessTypeQuery = (accessType === ACL.ALL) ? undefined :
|
||||
{inq: [accessType, ACL.ALL, ACL.EXECUTE]};
|
||||
|
||||
var req = new AccessRequest({model, property, accessType, registry: this.registry});
|
||||
const req = new AccessRequest({model, property, accessType, registry: this.registry});
|
||||
|
||||
var acls = this.getStaticACLs(model, property);
|
||||
let acls = this.getStaticACLs(model, property);
|
||||
|
||||
// resolved is an instance of AccessRequest
|
||||
var resolved = this.resolvePermission(acls, req);
|
||||
let resolved = this.resolvePermission(acls, req);
|
||||
|
||||
if (resolved && resolved.permission === ACL.DENY) {
|
||||
debug('Permission denied by statically resolved permission');
|
||||
|
@ -359,18 +360,18 @@ module.exports = function(ACL) {
|
|||
return callback.promise;
|
||||
}
|
||||
|
||||
var self = this;
|
||||
const self = this;
|
||||
this.find({where: {principalType: principalType, principalId: principalId,
|
||||
model: model, property: propertyQuery, accessType: accessTypeQuery}},
|
||||
function(err, dynACLs) {
|
||||
if (err) {
|
||||
return callback(err);
|
||||
}
|
||||
acls = acls.concat(dynACLs);
|
||||
// resolved is an instance of AccessRequest
|
||||
resolved = self.resolvePermission(acls, req);
|
||||
return callback(null, resolved);
|
||||
});
|
||||
function(err, dynACLs) {
|
||||
if (err) {
|
||||
return callback(err);
|
||||
}
|
||||
acls = acls.concat(dynACLs);
|
||||
// resolved is an instance of AccessRequest
|
||||
resolved = self.resolvePermission(acls, req);
|
||||
return callback(null, resolved);
|
||||
});
|
||||
return callback.promise;
|
||||
};
|
||||
|
||||
|
@ -430,33 +431,33 @@ module.exports = function(ACL) {
|
|||
*/
|
||||
ACL.checkAccessForContext = function(context, callback) {
|
||||
if (!callback) callback = utils.createPromiseCallback();
|
||||
var self = this;
|
||||
const self = this;
|
||||
self.resolveRelatedModels();
|
||||
var roleModel = self.roleModel;
|
||||
const roleModel = self.roleModel;
|
||||
|
||||
if (!(context instanceof AccessContext)) {
|
||||
context.registry = this.registry;
|
||||
context = new AccessContext(context);
|
||||
}
|
||||
|
||||
var authorizedRoles = {};
|
||||
var remotingContext = context.remotingContext;
|
||||
var model = context.model;
|
||||
var modelDefaultPermission = model && model.settings.defaultPermission;
|
||||
var property = context.property;
|
||||
var accessType = context.accessType;
|
||||
var modelName = context.modelName;
|
||||
let authorizedRoles = {};
|
||||
const remotingContext = context.remotingContext;
|
||||
const model = context.model;
|
||||
const modelDefaultPermission = model && model.settings.defaultPermission;
|
||||
const property = context.property;
|
||||
const accessType = context.accessType;
|
||||
const modelName = context.modelName;
|
||||
|
||||
var methodNames = context.methodNames;
|
||||
var propertyQuery = (property === ACL.ALL) ? undefined : {inq: methodNames.concat([ACL.ALL])};
|
||||
const methodNames = context.methodNames;
|
||||
const propertyQuery = (property === ACL.ALL) ? undefined : {inq: methodNames.concat([ACL.ALL])};
|
||||
|
||||
var accessTypeQuery = (accessType === ACL.ALL) ?
|
||||
const accessTypeQuery = (accessType === ACL.ALL) ?
|
||||
undefined :
|
||||
(accessType === ACL.REPLICATE) ?
|
||||
{inq: [ACL.REPLICATE, ACL.WRITE, ACL.ALL]} :
|
||||
{inq: [accessType, ACL.ALL]};
|
||||
|
||||
var req = new AccessRequest({
|
||||
const req = new AccessRequest({
|
||||
model: modelName,
|
||||
property,
|
||||
accessType,
|
||||
|
@ -474,22 +475,29 @@ module.exports = function(ACL) {
|
|||
return callback.promise;
|
||||
}
|
||||
|
||||
var effectiveACLs = [];
|
||||
var staticACLs = self.getStaticACLs(model.modelName, property);
|
||||
const effectiveACLs = [];
|
||||
const staticACLs = self.getStaticACLs(model.modelName, property);
|
||||
|
||||
this.find({where: {model: model.modelName, property: propertyQuery,
|
||||
accessType: accessTypeQuery}}, function(err, acls) {
|
||||
const query = {
|
||||
where: {
|
||||
model: {inq: [model.modelName, ACL.ALL]},
|
||||
property: propertyQuery,
|
||||
accessType: accessTypeQuery,
|
||||
},
|
||||
};
|
||||
|
||||
this.find(query, function(err, acls) {
|
||||
if (err) return callback(err);
|
||||
var inRoleTasks = [];
|
||||
const inRoleTasks = [];
|
||||
|
||||
acls = acls.concat(staticACLs);
|
||||
|
||||
acls.forEach(function(acl) {
|
||||
// Check exact matches
|
||||
for (var i = 0; i < context.principals.length; i++) {
|
||||
var p = context.principals[i];
|
||||
var typeMatch = p.type === acl.principalType;
|
||||
var idMatch = String(p.id) === String(acl.principalId);
|
||||
for (let i = 0; i < context.principals.length; i++) {
|
||||
const p = context.principals[i];
|
||||
const typeMatch = p.type === acl.principalType;
|
||||
const idMatch = String(p.id) === String(acl.principalId);
|
||||
if (typeMatch && idMatch) {
|
||||
effectiveACLs.push(acl);
|
||||
return;
|
||||
|
@ -517,7 +525,7 @@ module.exports = function(ACL) {
|
|||
if (err) return callback(err, null);
|
||||
|
||||
// resolved is an instance of AccessRequest
|
||||
var resolved = self.resolvePermission(effectiveACLs, req);
|
||||
const resolved = self.resolvePermission(effectiveACLs, req);
|
||||
debug('---Resolved---');
|
||||
resolved.debug();
|
||||
|
||||
|
@ -554,7 +562,7 @@ module.exports = function(ACL) {
|
|||
ACL.checkAccessForToken = function(token, model, modelId, method, callback) {
|
||||
assert(token, 'Access token is required');
|
||||
if (!callback) callback = utils.createPromiseCallback();
|
||||
var context = new AccessContext({
|
||||
const context = new AccessContext({
|
||||
registry: this.registry,
|
||||
accessToken: token,
|
||||
model: model,
|
||||
|
@ -572,7 +580,7 @@ module.exports = function(ACL) {
|
|||
|
||||
ACL.resolveRelatedModels = function() {
|
||||
if (!this.roleModel) {
|
||||
var reg = this.registry;
|
||||
const reg = this.registry;
|
||||
this.roleModel = reg.getModelByType('Role');
|
||||
this.roleMappingModel = reg.getModelByType('RoleMapping');
|
||||
this.userModel = reg.getModelByType('User');
|
||||
|
@ -599,22 +607,25 @@ module.exports = function(ACL) {
|
|||
break;
|
||||
case ACL.USER:
|
||||
this.userModel.findOne(
|
||||
{where: {or: [{username: id}, {email: id}, {id: id}]}}, cb);
|
||||
{where: {or: [{username: id}, {email: id}, {id: id}]}}, cb,
|
||||
);
|
||||
break;
|
||||
case ACL.APP:
|
||||
this.applicationModel.findOne(
|
||||
{where: {or: [{name: id}, {email: id}, {id: id}]}}, cb);
|
||||
{where: {or: [{name: id}, {email: id}, {id: id}]}}, cb,
|
||||
);
|
||||
break;
|
||||
default:
|
||||
// try resolving a user model with a name matching the principalType
|
||||
var userModel = this.registry.findModel(type);
|
||||
const userModel = this.registry.findModel(type);
|
||||
if (userModel) {
|
||||
userModel.findOne(
|
||||
{where: {or: [{username: id}, {email: id}, {id: id}]}},
|
||||
cb);
|
||||
cb,
|
||||
);
|
||||
} else {
|
||||
process.nextTick(function() {
|
||||
var err = new Error(g.f('Invalid principal type: %s', type));
|
||||
const err = new Error(g.f('Invalid principal type: %s', type));
|
||||
err.statusCode = 400;
|
||||
err.code = 'INVALID_PRINCIPAL_TYPE';
|
||||
cb(err);
|
||||
|
@ -635,7 +646,7 @@ module.exports = function(ACL) {
|
|||
*/
|
||||
ACL.isMappedToRole = function(principalType, principalId, role, cb) {
|
||||
cb = cb || utils.createPromiseCallback();
|
||||
var self = this;
|
||||
const self = this;
|
||||
this.resolvePrincipal(principalType, principalId,
|
||||
function(err, principal) {
|
||||
if (err) return cb(err);
|
||||
|
|
|
@ -1,26 +1,26 @@
|
|||
// Copyright IBM Corp. 2014,2016. All Rights Reserved.
|
||||
// Copyright IBM Corp. 2014,2019. All Rights Reserved.
|
||||
// Node module: loopback
|
||||
// This file is licensed under the MIT License.
|
||||
// License text available at https://opensource.org/licenses/MIT
|
||||
|
||||
'use strict';
|
||||
var assert = require('assert');
|
||||
var utils = require('../../lib/utils');
|
||||
const assert = require('assert');
|
||||
const utils = require('../../lib/utils');
|
||||
|
||||
/*!
|
||||
* Application management functions
|
||||
*/
|
||||
|
||||
var crypto = require('crypto');
|
||||
const crypto = require('crypto');
|
||||
|
||||
function generateKey(hmacKey, algorithm, encoding) {
|
||||
hmacKey = hmacKey || 'loopback';
|
||||
algorithm = algorithm || 'sha1';
|
||||
encoding = encoding || 'hex';
|
||||
var hmac = crypto.createHmac(algorithm, hmacKey);
|
||||
var buf = crypto.randomBytes(32);
|
||||
const hmac = crypto.createHmac(algorithm, hmacKey);
|
||||
const buf = crypto.randomBytes(32);
|
||||
hmac.update(buf);
|
||||
var key = hmac.digest(encoding);
|
||||
const key = hmac.digest(encoding);
|
||||
return key;
|
||||
}
|
||||
|
||||
|
@ -83,7 +83,7 @@ module.exports = function(Application) {
|
|||
return next();
|
||||
}
|
||||
|
||||
var app = ctx.instance;
|
||||
const app = ctx.instance;
|
||||
app.created = app.modified = new Date();
|
||||
if (!app.id) {
|
||||
app.id = generateKey('id', 'md5');
|
||||
|
@ -115,8 +115,8 @@ module.exports = function(Application) {
|
|||
}
|
||||
cb = cb || utils.createPromiseCallback();
|
||||
|
||||
var props = {owner: owner, name: name};
|
||||
for (var p in options) {
|
||||
const props = {owner: owner, name: name};
|
||||
for (const p in options) {
|
||||
if (!(p in props)) {
|
||||
props[p] = options[p];
|
||||
}
|
||||
|
@ -182,9 +182,9 @@ module.exports = function(Application) {
|
|||
cb(err, null);
|
||||
return cb.promise;
|
||||
}
|
||||
var result = null;
|
||||
var keyNames = ['clientKey', 'javaScriptKey', 'restApiKey', 'windowsKey', 'masterKey'];
|
||||
for (var i = 0; i < keyNames.length; i++) {
|
||||
let result = null;
|
||||
const keyNames = ['clientKey', 'javaScriptKey', 'restApiKey', 'windowsKey', 'masterKey'];
|
||||
for (let i = 0; i < keyNames.length; i++) {
|
||||
if (app[keyNames[i]] === key) {
|
||||
result = {
|
||||
application: app,
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// Copyright IBM Corp. 2014,2016. All Rights Reserved.
|
||||
// Copyright IBM Corp. 2014,2019. All Rights Reserved.
|
||||
// Node module: loopback
|
||||
// This file is licensed under the MIT License.
|
||||
// License text available at https://opensource.org/licenses/MIT
|
||||
|
@ -8,15 +8,15 @@
|
|||
*/
|
||||
|
||||
'use strict';
|
||||
var g = require('../../lib/globalize');
|
||||
var PersistedModel = require('../../lib/loopback').PersistedModel;
|
||||
var loopback = require('../../lib/loopback');
|
||||
var utils = require('../../lib/utils');
|
||||
var crypto = require('crypto');
|
||||
var CJSON = {stringify: require('canonical-json')};
|
||||
var async = require('async');
|
||||
var assert = require('assert');
|
||||
var debug = require('debug')('loopback:change');
|
||||
const g = require('../../lib/globalize');
|
||||
const PersistedModel = require('../../lib/loopback').PersistedModel;
|
||||
const loopback = require('../../lib/loopback');
|
||||
const utils = require('../../lib/utils');
|
||||
const crypto = require('crypto');
|
||||
const CJSON = {stringify: require('canonical-json')};
|
||||
const async = require('async');
|
||||
const assert = require('assert');
|
||||
const debug = require('debug')('loopback:change');
|
||||
|
||||
/**
|
||||
* Change list entry.
|
||||
|
@ -58,10 +58,10 @@ module.exports = function(Change) {
|
|||
|
||||
Change.setup = function() {
|
||||
PersistedModel.setup.call(this);
|
||||
var Change = this;
|
||||
const Change = this;
|
||||
|
||||
Change.getter.id = function() {
|
||||
var hasModel = this.modelName && this.modelId;
|
||||
const hasModel = this.modelName && this.modelId;
|
||||
if (!hasModel) return null;
|
||||
|
||||
return Change.idForModel(this.modelName, this.modelId);
|
||||
|
@ -80,12 +80,12 @@ module.exports = function(Change) {
|
|||
*/
|
||||
|
||||
Change.rectifyModelChanges = function(modelName, modelIds, callback) {
|
||||
var Change = this;
|
||||
var errors = [];
|
||||
const Change = this;
|
||||
const errors = [];
|
||||
|
||||
callback = callback || utils.createPromiseCallback();
|
||||
|
||||
var tasks = modelIds.map(function(id) {
|
||||
const tasks = modelIds.map(function(id) {
|
||||
return function(cb) {
|
||||
Change.findOrCreateChange(modelName, id, function(err, change) {
|
||||
if (err) return next(err);
|
||||
|
@ -106,13 +106,13 @@ module.exports = function(Change) {
|
|||
async.parallel(tasks, function(err) {
|
||||
if (err) return callback(err);
|
||||
if (errors.length) {
|
||||
var desc = errors
|
||||
const desc = errors
|
||||
.map(function(e) {
|
||||
return '#' + e.modelId + ' - ' + e.toString();
|
||||
})
|
||||
.join('\n');
|
||||
|
||||
var msg = g.f('Cannot rectify %s changes:\n%s', modelName, desc);
|
||||
const msg = g.f('Cannot rectify %s changes:\n%s', modelName, desc);
|
||||
err = new Error(msg);
|
||||
err.details = {errors: errors};
|
||||
return callback(err);
|
||||
|
@ -148,15 +148,15 @@ module.exports = function(Change) {
|
|||
Change.findOrCreateChange = function(modelName, modelId, callback) {
|
||||
assert(this.registry.findModel(modelName), modelName + ' does not exist');
|
||||
callback = callback || utils.createPromiseCallback();
|
||||
var id = this.idForModel(modelName, modelId);
|
||||
var Change = this;
|
||||
const id = this.idForModel(modelName, modelId);
|
||||
const Change = this;
|
||||
|
||||
this.findById(id, function(err, change) {
|
||||
if (err) return callback(err);
|
||||
if (change) {
|
||||
callback(null, change);
|
||||
} else {
|
||||
var ch = new Change({
|
||||
const ch = new Change({
|
||||
id: id,
|
||||
modelName: modelName,
|
||||
modelId: modelId,
|
||||
|
@ -177,8 +177,8 @@ module.exports = function(Change) {
|
|||
*/
|
||||
|
||||
Change.prototype.rectify = function(cb) {
|
||||
var change = this;
|
||||
var currentRev = this.rev;
|
||||
const change = this;
|
||||
const currentRev = this.rev;
|
||||
|
||||
change.debug('rectify change');
|
||||
|
||||
|
@ -216,7 +216,7 @@ module.exports = function(Change) {
|
|||
function(err, checkpoint) {
|
||||
if (err) return cb(err);
|
||||
doRectify(checkpoint, rev);
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -274,8 +274,8 @@ module.exports = function(Change) {
|
|||
|
||||
Change.prototype.currentRevision = function(cb) {
|
||||
cb = cb || utils.createPromiseCallback();
|
||||
var model = this.getModelCtor();
|
||||
var id = this.getModelId();
|
||||
const model = this.getModelCtor();
|
||||
const id = this.getModelId();
|
||||
model.findById(id, function(err, inst) {
|
||||
if (err) return cb(err);
|
||||
if (inst) {
|
||||
|
@ -345,8 +345,8 @@ module.exports = function(Change) {
|
|||
|
||||
Change.prototype.equals = function(change) {
|
||||
if (!change) return false;
|
||||
var thisRev = this.rev || null;
|
||||
var thatRev = change.rev || null;
|
||||
const thisRev = this.rev || null;
|
||||
const thatRev = change.rev || null;
|
||||
return thisRev === thatRev;
|
||||
};
|
||||
|
||||
|
@ -423,8 +423,8 @@ module.exports = function(Change) {
|
|||
callback(null, {deltas: [], conflicts: []});
|
||||
return callback.promise;
|
||||
}
|
||||
var remoteChangeIndex = {};
|
||||
var modelIds = [];
|
||||
const remoteChangeIndex = {};
|
||||
const modelIds = [];
|
||||
remoteChanges.forEach(function(ch) {
|
||||
modelIds.push(ch.modelId);
|
||||
remoteChangeIndex[ch.modelId] = new Change(ch);
|
||||
|
@ -439,18 +439,18 @@ module.exports = function(Change) {
|
|||
},
|
||||
}, function(err, allLocalChanges) {
|
||||
if (err) return callback(err);
|
||||
var deltas = [];
|
||||
var conflicts = [];
|
||||
var localModelIds = [];
|
||||
const deltas = [];
|
||||
const conflicts = [];
|
||||
const localModelIds = [];
|
||||
|
||||
var localChanges = allLocalChanges.filter(function(c) {
|
||||
const localChanges = allLocalChanges.filter(function(c) {
|
||||
return c.checkpoint >= since;
|
||||
});
|
||||
|
||||
localChanges.forEach(function(localChange) {
|
||||
localChange = new Change(localChange);
|
||||
localModelIds.push(localChange.modelId);
|
||||
var remoteChange = remoteChangeIndex[localChange.modelId];
|
||||
const remoteChange = remoteChangeIndex[localChange.modelId];
|
||||
if (remoteChange && !localChange.equals(remoteChange)) {
|
||||
if (remoteChange.conflictsWith(localChange)) {
|
||||
remoteChange.debug('remote conflict');
|
||||
|
@ -466,8 +466,8 @@ module.exports = function(Change) {
|
|||
modelIds.forEach(function(id) {
|
||||
if (localModelIds.indexOf(id) !== -1) return;
|
||||
|
||||
var d = remoteChangeIndex[id];
|
||||
var oldChange = allLocalChanges.filter(function(c) {
|
||||
const d = remoteChangeIndex[id];
|
||||
const oldChange = allLocalChanges.filter(function(c) {
|
||||
return c.modelId === id;
|
||||
})[0];
|
||||
|
||||
|
@ -495,14 +495,15 @@ module.exports = function(Change) {
|
|||
|
||||
Change.rectifyAll = function(cb) {
|
||||
debug('rectify all');
|
||||
var Change = this;
|
||||
const Change = this;
|
||||
// this should be optimized
|
||||
this.find(function(err, changes) {
|
||||
if (err) return cb(err);
|
||||
async.each(
|
||||
changes,
|
||||
function(c, next) { c.rectify(next); },
|
||||
cb);
|
||||
cb,
|
||||
);
|
||||
});
|
||||
};
|
||||
|
||||
|
@ -512,7 +513,7 @@ module.exports = function(Change) {
|
|||
*/
|
||||
|
||||
Change.getCheckpointModel = function() {
|
||||
var checkpointModel = this.Checkpoint;
|
||||
let checkpointModel = this.Checkpoint;
|
||||
if (checkpointModel) return checkpointModel;
|
||||
// FIXME(bajtos) This code creates multiple different models with the same
|
||||
// model name, which is not a valid supported usage of juggler's API.
|
||||
|
@ -525,7 +526,7 @@ module.exports = function(Change) {
|
|||
|
||||
Change.prototype.debug = function() {
|
||||
if (debug.enabled) {
|
||||
var args = Array.prototype.slice.call(arguments);
|
||||
const args = Array.prototype.slice.call(arguments);
|
||||
args[0] = args[0] + ' %s';
|
||||
args.push(this.modelName);
|
||||
debug.apply(this, args);
|
||||
|
@ -550,16 +551,16 @@ module.exports = function(Change) {
|
|||
|
||||
Change.prototype.getModelId = function() {
|
||||
// TODO(ritch) get rid of the need to create an instance
|
||||
var Model = this.getModelCtor();
|
||||
var id = this.modelId;
|
||||
var m = new Model();
|
||||
const Model = this.getModelCtor();
|
||||
const id = this.modelId;
|
||||
const m = new Model();
|
||||
m.setId(id);
|
||||
return m.getId();
|
||||
};
|
||||
|
||||
Change.prototype.getModel = function(callback) {
|
||||
var Model = this.constructor.settings.trackModel;
|
||||
var id = this.getModelId();
|
||||
const Model = this.constructor.settings.trackModel;
|
||||
const id = this.getModelId();
|
||||
Model.findById(id, callback);
|
||||
};
|
||||
|
||||
|
@ -594,10 +595,10 @@ module.exports = function(Change) {
|
|||
*/
|
||||
|
||||
Conflict.prototype.models = function(cb) {
|
||||
var conflict = this;
|
||||
var SourceModel = this.SourceModel;
|
||||
var TargetModel = this.TargetModel;
|
||||
var source, target;
|
||||
const conflict = this;
|
||||
const SourceModel = this.SourceModel;
|
||||
const TargetModel = this.TargetModel;
|
||||
let source, target;
|
||||
|
||||
async.parallel([
|
||||
getSourceModel,
|
||||
|
@ -636,8 +637,8 @@ module.exports = function(Change) {
|
|||
*/
|
||||
|
||||
Conflict.prototype.changes = function(cb) {
|
||||
var conflict = this;
|
||||
var sourceChange, targetChange;
|
||||
const conflict = this;
|
||||
let sourceChange, targetChange;
|
||||
|
||||
async.parallel([
|
||||
getSourceChange,
|
||||
|
@ -645,7 +646,7 @@ module.exports = function(Change) {
|
|||
], done);
|
||||
|
||||
function getSourceChange(cb) {
|
||||
var SourceModel = conflict.SourceModel;
|
||||
const SourceModel = conflict.SourceModel;
|
||||
SourceModel.findLastChange(conflict.modelId, function(err, change) {
|
||||
if (err) return cb(err);
|
||||
sourceChange = change;
|
||||
|
@ -654,7 +655,7 @@ module.exports = function(Change) {
|
|||
}
|
||||
|
||||
function getTargetChange(cb) {
|
||||
var TargetModel = conflict.TargetModel;
|
||||
const TargetModel = conflict.TargetModel;
|
||||
TargetModel.findLastChange(conflict.modelId, function(err, change) {
|
||||
if (err) return cb(err);
|
||||
targetChange = change;
|
||||
|
@ -683,7 +684,7 @@ module.exports = function(Change) {
|
|||
*/
|
||||
|
||||
Conflict.prototype.resolve = function(cb) {
|
||||
var conflict = this;
|
||||
const conflict = this;
|
||||
conflict.TargetModel.findLastChange(
|
||||
this.modelId,
|
||||
function(err, targetChange) {
|
||||
|
@ -691,8 +692,10 @@ module.exports = function(Change) {
|
|||
conflict.SourceModel.updateLastChange(
|
||||
conflict.modelId,
|
||||
{prev: targetChange.rev},
|
||||
cb);
|
||||
});
|
||||
cb,
|
||||
);
|
||||
},
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -715,16 +718,17 @@ module.exports = function(Change) {
|
|||
* @param {Error} err
|
||||
*/
|
||||
Conflict.prototype.resolveUsingTarget = function(cb) {
|
||||
var conflict = this;
|
||||
const conflict = this;
|
||||
|
||||
conflict.models(function(err, source, target) {
|
||||
if (err) return done(err);
|
||||
if (target === null) {
|
||||
return conflict.SourceModel.deleteById(conflict.modelId, done);
|
||||
}
|
||||
var inst = new conflict.SourceModel(
|
||||
const inst = new conflict.SourceModel(
|
||||
target.toObject(),
|
||||
{persisted: true});
|
||||
{persisted: true},
|
||||
);
|
||||
inst.save(done);
|
||||
});
|
||||
|
||||
|
@ -747,7 +751,7 @@ module.exports = function(Change) {
|
|||
* @returns {Conflict} A new Conflict instance.
|
||||
*/
|
||||
Conflict.prototype.swapParties = function() {
|
||||
var Ctor = this.constructor;
|
||||
const Ctor = this.constructor;
|
||||
return new Ctor(this.modelId, this.TargetModel, this.SourceModel);
|
||||
};
|
||||
|
||||
|
@ -761,14 +765,14 @@ module.exports = function(Change) {
|
|||
*/
|
||||
|
||||
Conflict.prototype.resolveManually = function(data, cb) {
|
||||
var conflict = this;
|
||||
const conflict = this;
|
||||
if (!data) {
|
||||
return conflict.SourceModel.deleteById(conflict.modelId, done);
|
||||
}
|
||||
|
||||
conflict.models(function(err, source, target) {
|
||||
if (err) return done(err);
|
||||
var inst = source || new conflict.SourceModel(target);
|
||||
const inst = source || new conflict.SourceModel(target);
|
||||
inst.setAttributes(data);
|
||||
inst.save(function(err) {
|
||||
if (err) return done(err);
|
||||
|
@ -797,11 +801,11 @@ module.exports = function(Change) {
|
|||
*/
|
||||
|
||||
Conflict.prototype.type = function(cb) {
|
||||
var conflict = this;
|
||||
const conflict = this;
|
||||
this.changes(function(err, sourceChange, targetChange) {
|
||||
if (err) return cb(err);
|
||||
var sourceChangeType = sourceChange.type();
|
||||
var targetChangeType = targetChange.type();
|
||||
const sourceChangeType = sourceChange.type();
|
||||
const targetChangeType = targetChange.type();
|
||||
if (sourceChangeType === Change.UPDATE && targetChangeType === Change.UPDATE) {
|
||||
return cb(null, Change.UPDATE);
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// Copyright IBM Corp. 2014,2016. All Rights Reserved.
|
||||
// Copyright IBM Corp. 2014,2019. All Rights Reserved.
|
||||
// Node module: loopback
|
||||
// This file is licensed under the MIT License.
|
||||
// License text available at https://opensource.org/licenses/MIT
|
||||
|
@ -8,7 +8,7 @@
|
|||
*/
|
||||
|
||||
'use strict';
|
||||
var assert = require('assert');
|
||||
const assert = require('assert');
|
||||
|
||||
/**
|
||||
* Checkpoint list entry.
|
||||
|
@ -35,15 +35,15 @@ module.exports = function(Checkpoint) {
|
|||
* @param {Number} checkpoint The current checkpoint seq
|
||||
*/
|
||||
Checkpoint.current = function(cb) {
|
||||
var Checkpoint = this;
|
||||
const Checkpoint = this;
|
||||
Checkpoint._getSingleton(function(err, cp) {
|
||||
cb(err, cp.seq);
|
||||
});
|
||||
};
|
||||
|
||||
Checkpoint._getSingleton = function(cb) {
|
||||
var query = {limit: 1}; // match all instances, return only one
|
||||
var initialData = {seq: 1};
|
||||
const query = {limit: 1}; // match all instances, return only one
|
||||
const initialData = {seq: 1};
|
||||
this.findOrCreate(query, initialData, cb);
|
||||
};
|
||||
|
||||
|
@ -54,10 +54,10 @@ module.exports = function(Checkpoint) {
|
|||
* @param {Object} checkpoint The current checkpoint
|
||||
*/
|
||||
Checkpoint.bumpLastSeq = function(cb) {
|
||||
var Checkpoint = this;
|
||||
const Checkpoint = this;
|
||||
Checkpoint._getSingleton(function(err, cp) {
|
||||
if (err) return cb(err);
|
||||
var originalSeq = cp.seq;
|
||||
const originalSeq = cp.seq;
|
||||
cp.seq++;
|
||||
// Update the checkpoint but only if it was not changed under our hands
|
||||
Checkpoint.updateAll({id: cp.id, seq: originalSeq}, {seq: cp.seq}, function(err, info) {
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
// Copyright IBM Corp. 2014,2016. All Rights Reserved.
|
||||
// Copyright IBM Corp. 2014,2019. All Rights Reserved.
|
||||
// Node module: loopback
|
||||
// This file is licensed under the MIT License.
|
||||
// License text available at https://opensource.org/licenses/MIT
|
||||
|
||||
'use strict';
|
||||
var g = require('../../lib/globalize');
|
||||
const g = require('../../lib/globalize');
|
||||
|
||||
/**
|
||||
* Email model. Extends LoopBack base [Model](#model-new-model).
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
// Copyright IBM Corp. 2014,2016. All Rights Reserved.
|
||||
// Copyright IBM Corp. 2016,2019. All Rights Reserved.
|
||||
// Node module: loopback
|
||||
// This file is licensed under the MIT License.
|
||||
// License text available at https://opensource.org/licenses/MIT
|
||||
|
||||
'use strict';
|
||||
var g = require('../../lib/globalize');
|
||||
const g = require('../../lib/globalize');
|
||||
|
||||
/**
|
||||
* Data model for key-value databases.
|
||||
|
@ -223,19 +223,20 @@ module.exports = function(KeyValueModel) {
|
|||
function throwNotAttached(modelName, methodName) {
|
||||
throw new Error(g.f(
|
||||
'Cannot call %s.%s(). ' +
|
||||
'The %s method has not been setup. ' +
|
||||
'The %s method has not been setup. ' +
|
||||
'The {{KeyValueModel}} has not been correctly attached ' +
|
||||
'to a {{DataSource}}!',
|
||||
modelName, methodName, methodName));
|
||||
modelName, methodName, methodName,
|
||||
));
|
||||
}
|
||||
|
||||
function convertNullToNotFoundError(ctx, cb) {
|
||||
if (ctx.result !== null) return cb();
|
||||
|
||||
var modelName = ctx.method.sharedClass.name;
|
||||
var id = ctx.getArgByName('id');
|
||||
var msg = g.f('Unknown "%s" {{key}} "%s".', modelName, id);
|
||||
var error = new Error(msg);
|
||||
const modelName = ctx.method.sharedClass.name;
|
||||
const id = ctx.getArgByName('id');
|
||||
const msg = g.f('Unknown "%s" {{key}} "%s".', modelName, id);
|
||||
const error = new Error(msg);
|
||||
error.statusCode = error.status = 404;
|
||||
error.code = 'KEY_NOT_FOUND';
|
||||
cb(error);
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
// Copyright IBM Corp. 2014,2015. All Rights Reserved.
|
||||
// Copyright IBM Corp. 2014,2019. All Rights Reserved.
|
||||
// Node module: loopback
|
||||
// This file is licensed under the MIT License.
|
||||
// License text available at https://opensource.org/licenses/MIT
|
||||
|
||||
'use strict';
|
||||
var loopback = require('../../lib/loopback');
|
||||
var utils = require('../../lib/utils');
|
||||
const loopback = require('../../lib/loopback');
|
||||
const utils = require('../../lib/utils');
|
||||
|
||||
/**
|
||||
* The `RoleMapping` model extends from the built in `loopback.Model` type.
|
||||
|
@ -26,7 +26,7 @@ module.exports = function(RoleMapping) {
|
|||
|
||||
RoleMapping.resolveRelatedModels = function() {
|
||||
if (!this.userModel) {
|
||||
var reg = this.registry;
|
||||
const reg = this.registry;
|
||||
this.roleModel = reg.getModelByType('Role');
|
||||
this.userModel = reg.getModelByType('User');
|
||||
this.applicationModel = reg.getModelByType('Application');
|
||||
|
@ -44,7 +44,7 @@ module.exports = function(RoleMapping) {
|
|||
this.constructor.resolveRelatedModels();
|
||||
|
||||
if (this.principalType === RoleMapping.APPLICATION) {
|
||||
var applicationModel = this.constructor.applicationModel;
|
||||
const applicationModel = this.constructor.applicationModel;
|
||||
applicationModel.findById(this.principalId, callback);
|
||||
} else {
|
||||
process.nextTick(function() {
|
||||
|
@ -63,7 +63,7 @@ module.exports = function(RoleMapping) {
|
|||
RoleMapping.prototype.user = function(callback) {
|
||||
callback = callback || utils.createPromiseCallback();
|
||||
this.constructor.resolveRelatedModels();
|
||||
var userModel;
|
||||
let userModel;
|
||||
|
||||
if (this.principalType === RoleMapping.USER) {
|
||||
userModel = this.constructor.userModel;
|
||||
|
@ -94,7 +94,7 @@ module.exports = function(RoleMapping) {
|
|||
this.constructor.resolveRelatedModels();
|
||||
|
||||
if (this.principalType === RoleMapping.ROLE) {
|
||||
var roleModel = this.constructor.roleModel;
|
||||
const roleModel = this.constructor.roleModel;
|
||||
roleModel.findById(this.principalId, callback);
|
||||
} else {
|
||||
process.nextTick(function() {
|
||||
|
|
|
@ -1,18 +1,18 @@
|
|||
// Copyright IBM Corp. 2014,2016. All Rights Reserved.
|
||||
// Copyright IBM Corp. 2014,2019. All Rights Reserved.
|
||||
// Node module: loopback
|
||||
// This file is licensed under the MIT License.
|
||||
// License text available at https://opensource.org/licenses/MIT
|
||||
|
||||
'use strict';
|
||||
var loopback = require('../../lib/loopback');
|
||||
var debug = require('debug')('loopback:security:role');
|
||||
var assert = require('assert');
|
||||
var async = require('async');
|
||||
var utils = require('../../lib/utils');
|
||||
var ctx = require('../../lib/access-context');
|
||||
var AccessContext = ctx.AccessContext;
|
||||
var Principal = ctx.Principal;
|
||||
var RoleMapping = loopback.RoleMapping;
|
||||
const loopback = require('../../lib/loopback');
|
||||
const debug = require('debug')('loopback:security:role');
|
||||
const assert = require('assert');
|
||||
const async = require('async');
|
||||
const utils = require('../../lib/utils');
|
||||
const ctx = require('../../lib/access-context');
|
||||
const AccessContext = ctx.AccessContext;
|
||||
const Principal = ctx.Principal;
|
||||
const RoleMapping = loopback.RoleMapping;
|
||||
|
||||
assert(RoleMapping, 'RoleMapping model must be defined before Role model');
|
||||
|
||||
|
@ -24,7 +24,7 @@ assert(RoleMapping, 'RoleMapping model must be defined before Role model');
|
|||
module.exports = function(Role) {
|
||||
Role.resolveRelatedModels = function() {
|
||||
if (!this.userModel) {
|
||||
var reg = this.registry;
|
||||
const reg = this.registry;
|
||||
this.roleMappingModel = reg.getModelByType('RoleMapping');
|
||||
this.userModel = reg.getModelByType('User');
|
||||
this.applicationModel = reg.getModelByType('Application');
|
||||
|
@ -74,29 +74,29 @@ module.exports = function(Role) {
|
|||
query.where = query.where || {};
|
||||
|
||||
roleModel.resolveRelatedModels();
|
||||
var relsToModels = {
|
||||
const relsToModels = {
|
||||
users: roleModel.userModel,
|
||||
applications: roleModel.applicationModel,
|
||||
roles: roleModel,
|
||||
};
|
||||
|
||||
var ACL = loopback.ACL;
|
||||
var relsToTypes = {
|
||||
const ACL = loopback.ACL;
|
||||
const relsToTypes = {
|
||||
users: ACL.USER,
|
||||
applications: ACL.APP,
|
||||
roles: ACL.ROLE,
|
||||
};
|
||||
|
||||
var principalModel = relsToModels[rel];
|
||||
var principalType = relsToTypes[rel];
|
||||
let principalModel = relsToModels[rel];
|
||||
let principalType = relsToTypes[rel];
|
||||
|
||||
// redefine user model and user type if user principalType is custom (available and not "USER")
|
||||
var isCustomUserPrincipalType = rel === 'users' &&
|
||||
const isCustomUserPrincipalType = rel === 'users' &&
|
||||
query.where.principalType &&
|
||||
query.where.principalType !== RoleMapping.USER;
|
||||
|
||||
if (isCustomUserPrincipalType) {
|
||||
var registry = this.constructor.registry;
|
||||
const registry = this.constructor.registry;
|
||||
principalModel = registry.findModel(query.where.principalType);
|
||||
principalType = query.where.principalType;
|
||||
}
|
||||
|
@ -133,11 +133,10 @@ module.exports = function(Role) {
|
|||
roleModel.roleMappingModel.find({
|
||||
where: {roleId: context.id, principalType: principalType},
|
||||
}, function(err, mappings) {
|
||||
var ids;
|
||||
if (err) {
|
||||
return callback(err);
|
||||
}
|
||||
ids = mappings.map(function(m) {
|
||||
const ids = mappings.map(function(m) {
|
||||
return m.principalId;
|
||||
});
|
||||
query.where = query.where || {};
|
||||
|
@ -177,18 +176,18 @@ module.exports = function(Role) {
|
|||
});
|
||||
return;
|
||||
}
|
||||
var modelClass = context.model;
|
||||
var modelId = context.modelId;
|
||||
var user = context.getUser();
|
||||
var userId = user && user.id;
|
||||
var principalType = user && user.principalType;
|
||||
var opts = {accessToken: context.accessToken};
|
||||
const modelClass = context.model;
|
||||
const modelId = context.modelId;
|
||||
const user = context.getUser();
|
||||
const userId = user && user.id;
|
||||
const principalType = user && user.principalType;
|
||||
const opts = {accessToken: context.accessToken};
|
||||
Role.isOwner(modelClass, modelId, userId, principalType, opts, callback);
|
||||
});
|
||||
|
||||
function isUserClass(modelClass) {
|
||||
if (!modelClass) return false;
|
||||
var User = modelClass.modelBuilder.models.User;
|
||||
const User = modelClass.modelBuilder.models.User;
|
||||
if (!User) return false;
|
||||
return modelClass == User || modelClass.prototype instanceof User;
|
||||
}
|
||||
|
@ -222,6 +221,8 @@ module.exports = function(Role) {
|
|||
* @promise
|
||||
*/
|
||||
Role.isOwner = function isOwner(modelClass, modelId, userId, principalType, options, callback) {
|
||||
const _this = this;
|
||||
|
||||
if (!callback && typeof options === 'function') {
|
||||
callback = options;
|
||||
options = {};
|
||||
|
@ -238,8 +239,29 @@ module.exports = function(Role) {
|
|||
debug('isOwner(): %s %s userId: %s principalType: %s',
|
||||
modelClass && modelClass.modelName, modelId, userId, principalType);
|
||||
|
||||
// Return false if userId is missing
|
||||
// Resolve isOwner false if userId is missing
|
||||
if (!userId) {
|
||||
debug('isOwner(): no user id was set, returning false');
|
||||
process.nextTick(function() {
|
||||
callback(null, false);
|
||||
});
|
||||
return callback.promise;
|
||||
}
|
||||
|
||||
// At this stage, principalType is valid in one of 2 following condition:
|
||||
// 1. the app has a single user model and principalType is 'USER'
|
||||
// 2. the app has multiple user models and principalType is not 'USER'
|
||||
// multiple user models
|
||||
const isMultipleUsers = _isMultipleUsers();
|
||||
const isPrincipalTypeValid =
|
||||
(!isMultipleUsers && principalType === Principal.USER) ||
|
||||
(isMultipleUsers && principalType !== Principal.USER);
|
||||
|
||||
debug('isOwner(): isMultipleUsers?', isMultipleUsers,
|
||||
'isPrincipalTypeValid?', isPrincipalTypeValid);
|
||||
|
||||
// Resolve isOwner false if principalType is invalid
|
||||
if (!isPrincipalTypeValid) {
|
||||
process.nextTick(function() {
|
||||
callback(null, false);
|
||||
});
|
||||
|
@ -248,14 +270,15 @@ module.exports = function(Role) {
|
|||
|
||||
// Is the modelClass User or a subclass of User?
|
||||
if (isUserClass(modelClass)) {
|
||||
var userModelName = modelClass.modelName;
|
||||
const userModelName = modelClass.modelName;
|
||||
// matching ids is enough if principalType is USER or matches given user model name
|
||||
if (principalType === Principal.USER || principalType === userModelName) {
|
||||
process.nextTick(function() {
|
||||
callback(null, matches(modelId, userId));
|
||||
});
|
||||
return callback.promise;
|
||||
}
|
||||
return callback.promise;
|
||||
// otherwise continue with the regular owner resolution
|
||||
}
|
||||
|
||||
modelClass.findById(modelId, options, function(err, inst) {
|
||||
|
@ -265,26 +288,48 @@ module.exports = function(Role) {
|
|||
}
|
||||
debug('Model found: %j', inst);
|
||||
|
||||
// Historically, for principalType USER, we were resolving isOwner()
|
||||
// as true if the model has "userId" or "owner" property matching
|
||||
// id of the current user (principalId), even though there was no
|
||||
// belongsTo relation set up.
|
||||
var ownerId = inst.userId || inst.owner;
|
||||
const ownerRelations = modelClass.settings.ownerRelations;
|
||||
if (!ownerRelations) {
|
||||
return legacyOwnershipCheck(inst);
|
||||
} else {
|
||||
return checkOwnership(inst);
|
||||
}
|
||||
});
|
||||
return callback.promise;
|
||||
|
||||
// NOTE Historically, for principalType USER, we were resolving isOwner()
|
||||
// as true if the model has "userId" or "owner" property matching
|
||||
// id of the current user (principalId), even though there was no
|
||||
// belongsTo relation set up.
|
||||
// Additionaly, the original implementation did not support the
|
||||
// possibility for a model to have multiple related users: when
|
||||
// testing belongsTo relations, the first related user failing the
|
||||
// ownership check induced the whole isOwner() to resolve as false.
|
||||
// This behaviour will be pruned at next LoopBack major release.
|
||||
function legacyOwnershipCheck(inst) {
|
||||
const ownerId = inst.userId || inst.owner;
|
||||
if (principalType === Principal.USER && ownerId && 'function' !== typeof ownerId) {
|
||||
return callback(null, matches(ownerId, userId));
|
||||
}
|
||||
|
||||
// Try to follow belongsTo
|
||||
for (var r in modelClass.relations) {
|
||||
var rel = modelClass.relations[r];
|
||||
for (const r in modelClass.relations) {
|
||||
const rel = modelClass.relations[r];
|
||||
// relation should be belongsTo and target a User based class
|
||||
var belongsToUser = rel.type === 'belongsTo' && isUserClass(rel.modelTo);
|
||||
const belongsToUser = rel.type === 'belongsTo' && isUserClass(rel.modelTo);
|
||||
if (!belongsToUser) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// checking related user
|
||||
var userModelName = rel.modelTo.modelName;
|
||||
if (principalType === Principal.USER || principalType === userModelName) {
|
||||
const relatedUser = rel.modelTo;
|
||||
const userModelName = relatedUser.modelName;
|
||||
const isMultipleUsers = _isMultipleUsers(relatedUser);
|
||||
// a relation can be considered for isOwner resolution if:
|
||||
// 1. the app has a single user model and principalType is 'USER'
|
||||
// 2. the app has multiple user models and principalType is the related user model name
|
||||
if ((!isMultipleUsers && principalType === Principal.USER) ||
|
||||
(isMultipleUsers && principalType === userModelName)) {
|
||||
debug('Checking relation %s to %s: %j', r, userModelName, rel);
|
||||
inst[r](processRelatedUser);
|
||||
return;
|
||||
|
@ -295,15 +340,72 @@ module.exports = function(Role) {
|
|||
callback(null, false);
|
||||
|
||||
function processRelatedUser(err, user) {
|
||||
if (!err && user) {
|
||||
debug('User found: %j', user.id);
|
||||
callback(null, matches(user.id, userId));
|
||||
} else {
|
||||
callback(err, false);
|
||||
if (err || !user) return callback(err, false);
|
||||
|
||||
debug('User found: %j', user.id);
|
||||
callback(null, matches(user.id, userId));
|
||||
}
|
||||
}
|
||||
|
||||
function checkOwnership(inst) {
|
||||
const ownerRelations = inst.constructor.settings.ownerRelations;
|
||||
// collecting related users
|
||||
const relWithUsers = [];
|
||||
for (const r in modelClass.relations) {
|
||||
const rel = modelClass.relations[r];
|
||||
// relation should be belongsTo and target a User based class
|
||||
if (rel.type !== 'belongsTo' || !isUserClass(rel.modelTo)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// checking related user
|
||||
const relatedUser = rel.modelTo;
|
||||
const userModelName = relatedUser.modelName;
|
||||
const isMultipleUsers = _isMultipleUsers(relatedUser);
|
||||
// a relation can be considered for isOwner resolution if:
|
||||
// 1. the app has a single user model and principalType is 'USER'
|
||||
// 2. the app has multiple user models and principalType is the related user model name
|
||||
// In addition, if an array of relations if provided with the ownerRelations option,
|
||||
// then the given relation name is further checked against this array
|
||||
if ((!isMultipleUsers && principalType === Principal.USER) ||
|
||||
(isMultipleUsers && principalType === userModelName)) {
|
||||
debug('Checking relation %s to %s: %j', r, userModelName, rel);
|
||||
if (ownerRelations === true) {
|
||||
relWithUsers.push(r);
|
||||
} else if (Array.isArray(ownerRelations) && ownerRelations.indexOf(r) !== -1) {
|
||||
relWithUsers.push(r);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
return callback.promise;
|
||||
if (relWithUsers.length === 0) {
|
||||
debug('No matching belongsTo relation found for model %j and user: %j principalType: %j',
|
||||
modelId, userId, principalType);
|
||||
return callback(null, false);
|
||||
}
|
||||
|
||||
// check related users: someSeries is used to avoid spamming the db
|
||||
async.someSeries(relWithUsers, processRelation, callback);
|
||||
|
||||
function processRelation(r, cb) {
|
||||
inst[r](function processRelatedUser(err, user) {
|
||||
if (err || !user) return cb(err, false);
|
||||
|
||||
debug('User found: %j (through %j)', user.id, r);
|
||||
cb(null, matches(user.id, userId));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// A helper function to check if the app user config is multiple users or
|
||||
// single user. It can be used with or without a reference user model.
|
||||
// In case no user model is provided, we use the registry to get any of the
|
||||
// user model by type. The relation with AccessToken is used to check
|
||||
// if polymorphism is used, and thus if multiple users.
|
||||
function _isMultipleUsers(userModel) {
|
||||
const oneOfUserModels = userModel || _this.registry.getModelByType('User');
|
||||
const accessTokensRel = oneOfUserModels.relations.accessTokens;
|
||||
return !!(accessTokensRel && accessTokensRel.polymorphic);
|
||||
}
|
||||
};
|
||||
|
||||
Role.registerResolver(Role.AUTHENTICATED, function(role, context, callback) {
|
||||
|
@ -377,15 +479,15 @@ module.exports = function(Role) {
|
|||
debug('isInRole(): %s', role);
|
||||
context.debug();
|
||||
|
||||
var resolver = Role.resolvers[role];
|
||||
const resolver = Role.resolvers[role];
|
||||
if (resolver) {
|
||||
debug('Custom resolver found for role %s', role);
|
||||
|
||||
var promise = resolver(role, context, callback);
|
||||
const promise = resolver(role, context, callback);
|
||||
if (promise && typeof promise.then === 'function') {
|
||||
promise.then(
|
||||
function(result) { callback(null, result); },
|
||||
callback
|
||||
callback,
|
||||
);
|
||||
}
|
||||
return callback.promise;
|
||||
|
@ -399,9 +501,9 @@ module.exports = function(Role) {
|
|||
return callback.promise;
|
||||
}
|
||||
|
||||
var inRole = context.principals.some(function(p) {
|
||||
var principalType = p.type || undefined;
|
||||
var principalId = p.id || undefined;
|
||||
const inRole = context.principals.some(function(p) {
|
||||
const principalType = p.type || undefined;
|
||||
const principalId = p.id || undefined;
|
||||
|
||||
// Check if it's the same role
|
||||
return principalType === RoleMapping.ROLE && principalId === role;
|
||||
|
@ -415,7 +517,7 @@ module.exports = function(Role) {
|
|||
return callback.promise;
|
||||
}
|
||||
|
||||
var roleMappingModel = this.roleMappingModel;
|
||||
const roleMappingModel = this.roleMappingModel;
|
||||
this.findOne({where: {name: role}}, function(err, result) {
|
||||
if (err) {
|
||||
if (callback) callback(err);
|
||||
|
@ -429,10 +531,10 @@ module.exports = function(Role) {
|
|||
|
||||
// Iterate through the list of principals
|
||||
async.some(context.principals, function(p, done) {
|
||||
var principalType = p.type || undefined;
|
||||
var principalId = p.id || undefined;
|
||||
var roleId = result.id.toString();
|
||||
var principalIdIsString = typeof principalId === 'string';
|
||||
const principalType = p.type || undefined;
|
||||
let principalId = p.id || undefined;
|
||||
const roleId = result.id.toString();
|
||||
const principalIdIsString = typeof principalId === 'string';
|
||||
|
||||
if (principalId !== null && principalId !== undefined && !principalIdIsString) {
|
||||
principalId = principalId.toString();
|
||||
|
@ -441,10 +543,10 @@ module.exports = function(Role) {
|
|||
if (principalType && principalId) {
|
||||
roleMappingModel.findOne({where: {roleId: roleId,
|
||||
principalType: principalType, principalId: principalId}},
|
||||
function(err, result) {
|
||||
debug('Role mapping found: %j', result);
|
||||
done(!err && result); // The only arg is the result
|
||||
});
|
||||
function(err, result) {
|
||||
debug('Role mapping found: %j', result);
|
||||
done(!err && result); // The only arg is the result
|
||||
});
|
||||
} else {
|
||||
process.nextTick(function() {
|
||||
done(false);
|
||||
|
@ -482,18 +584,18 @@ module.exports = function(Role) {
|
|||
if (!(context instanceof AccessContext)) {
|
||||
context = new AccessContext(context);
|
||||
}
|
||||
var roles = [];
|
||||
const roles = [];
|
||||
this.resolveRelatedModels();
|
||||
|
||||
var addRole = function(role) {
|
||||
const addRole = function(role) {
|
||||
if (role && roles.indexOf(role) === -1) {
|
||||
roles.push(role);
|
||||
}
|
||||
};
|
||||
|
||||
var self = this;
|
||||
const self = this;
|
||||
// Check against the smart roles
|
||||
var inRoleTasks = [];
|
||||
const inRoleTasks = [];
|
||||
Object.keys(Role.resolvers).forEach(function(role) {
|
||||
inRoleTasks.push(function(done) {
|
||||
self.isInRole(role, context, function(err, inRole) {
|
||||
|
@ -510,11 +612,11 @@ module.exports = function(Role) {
|
|||
});
|
||||
});
|
||||
|
||||
var roleMappingModel = this.roleMappingModel;
|
||||
const roleMappingModel = this.roleMappingModel;
|
||||
context.principals.forEach(function(p) {
|
||||
// Check against the role mappings
|
||||
var principalType = p.type || undefined;
|
||||
var principalId = p.id == null ? undefined : p.id;
|
||||
const principalType = p.type || undefined;
|
||||
let principalId = p.id == null ? undefined : p.id;
|
||||
|
||||
if (typeof principalId !== 'string' && principalId != null) {
|
||||
principalId = principalId.toString();
|
||||
|
@ -528,7 +630,7 @@ module.exports = function(Role) {
|
|||
if (principalType && principalId) {
|
||||
// Please find() treat undefined matches all values
|
||||
inRoleTasks.push(function(done) {
|
||||
var filter = {where: {principalType: principalType, principalId: principalId}};
|
||||
const filter = {where: {principalType: principalType, principalId: principalId}};
|
||||
if (options.returnOnlyRoleNames === true) {
|
||||
filter.include = ['role'];
|
||||
}
|
||||
|
@ -539,7 +641,7 @@ module.exports = function(Role) {
|
|||
return;
|
||||
}
|
||||
mappings.forEach(function(m) {
|
||||
var role;
|
||||
let role;
|
||||
if (options.returnOnlyRoleNames === true) {
|
||||
role = m.toJSON().role.name;
|
||||
} else {
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
// Copyright IBM Corp. 2014,2016. All Rights Reserved.
|
||||
// Copyright IBM Corp. 2014,2019. All Rights Reserved.
|
||||
// Node module: loopback
|
||||
// This file is licensed under the MIT License.
|
||||
// License text available at https://opensource.org/licenses/MIT
|
||||
|
||||
'use strict';
|
||||
var assert = require('assert');
|
||||
var loopback = require('../../lib/loopback');
|
||||
const assert = require('assert');
|
||||
const loopback = require('../../lib/loopback');
|
||||
|
||||
/**
|
||||
* Resource owner grants/delegates permissions to client applications
|
||||
|
@ -21,7 +21,7 @@ var loopback = require('../../lib/loopback');
|
|||
module.exports = function(Scope) {
|
||||
Scope.resolveRelatedModels = function() {
|
||||
if (!this.aclModel) {
|
||||
var reg = this.registry;
|
||||
const reg = this.registry;
|
||||
this.aclModel = reg.getModelByType(loopback.ACL);
|
||||
}
|
||||
};
|
||||
|
@ -38,7 +38,7 @@ module.exports = function(Scope) {
|
|||
*/
|
||||
Scope.checkPermission = function(scope, model, property, accessType, callback) {
|
||||
this.resolveRelatedModels();
|
||||
var aclModel = this.aclModel;
|
||||
const aclModel = this.aclModel;
|
||||
assert(aclModel,
|
||||
'ACL model must be defined before Scope.checkPermission is called');
|
||||
|
||||
|
@ -47,7 +47,8 @@ module.exports = function(Scope) {
|
|||
if (callback) callback(err);
|
||||
} else {
|
||||
aclModel.checkPermission(
|
||||
aclModel.SCOPE, scope.id, model, property, accessType, callback);
|
||||
aclModel.SCOPE, scope.id, model, property, accessType, callback,
|
||||
);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// Copyright IBM Corp. 2014,2016. All Rights Reserved.
|
||||
// Copyright IBM Corp. 2014,2019. All Rights Reserved.
|
||||
// Node module: loopback
|
||||
// This file is licensed under the MIT License.
|
||||
// License text available at https://opensource.org/licenses/MIT
|
||||
|
@ -8,18 +8,18 @@
|
|||
*/
|
||||
|
||||
'use strict';
|
||||
var g = require('../../lib/globalize');
|
||||
var isEmail = require('isemail');
|
||||
var loopback = require('../../lib/loopback');
|
||||
var utils = require('../../lib/utils');
|
||||
var path = require('path');
|
||||
var qs = require('querystring');
|
||||
var SALT_WORK_FACTOR = 10;
|
||||
var crypto = require('crypto');
|
||||
const g = require('../../lib/globalize');
|
||||
const isEmail = require('isemail');
|
||||
const loopback = require('../../lib/loopback');
|
||||
const utils = require('../../lib/utils');
|
||||
const path = require('path');
|
||||
const qs = require('querystring');
|
||||
const SALT_WORK_FACTOR = 10;
|
||||
const crypto = require('crypto');
|
||||
// bcrypt's max length is 72 bytes;
|
||||
// See https://github.com/kelektiv/node.bcrypt.js/blob/45f498ef6dc6e8234e58e07834ce06a50ff16352/src/node_blf.h#L59
|
||||
var MAX_PASSWORD_LENGTH = 72;
|
||||
var bcrypt;
|
||||
const MAX_PASSWORD_LENGTH = 72;
|
||||
let bcrypt;
|
||||
try {
|
||||
// Try the native module first
|
||||
bcrypt = require('bcrypt');
|
||||
|
@ -32,12 +32,12 @@ try {
|
|||
bcrypt = require('bcryptjs');
|
||||
}
|
||||
|
||||
var DEFAULT_TTL = 1209600; // 2 weeks in seconds
|
||||
var DEFAULT_RESET_PW_TTL = 15 * 60; // 15 mins in seconds
|
||||
var DEFAULT_MAX_TTL = 31556926; // 1 year in seconds
|
||||
var assert = require('assert');
|
||||
const DEFAULT_TTL = 1209600; // 2 weeks in seconds
|
||||
const DEFAULT_RESET_PW_TTL = 15 * 60; // 15 mins in seconds
|
||||
const DEFAULT_MAX_TTL = 31556926; // 1 year in seconds
|
||||
const assert = require('assert');
|
||||
|
||||
var debug = require('debug')('loopback:user');
|
||||
const debug = require('debug')('loopback:user');
|
||||
|
||||
/**
|
||||
* Built-in User model.
|
||||
|
@ -123,18 +123,18 @@ module.exports = function(User) {
|
|||
tokenData = {};
|
||||
}
|
||||
|
||||
var userSettings = this.constructor.settings;
|
||||
const userSettings = this.constructor.settings;
|
||||
tokenData.ttl = Math.min(tokenData.ttl || userSettings.ttl, userSettings.maxTTL);
|
||||
this.accessTokens.create(tokenData, options, cb);
|
||||
return cb.promise;
|
||||
};
|
||||
|
||||
function splitPrincipal(name, realmDelimiter) {
|
||||
var parts = [null, name];
|
||||
const parts = [null, name];
|
||||
if (!realmDelimiter) {
|
||||
return parts;
|
||||
}
|
||||
var index = name.indexOf(realmDelimiter);
|
||||
const index = name.indexOf(realmDelimiter);
|
||||
if (index !== -1) {
|
||||
parts[0] = name.substring(0, index);
|
||||
parts[1] = name.substring(index + realmDelimiter.length);
|
||||
|
@ -150,7 +150,7 @@ module.exports = function(User) {
|
|||
* @returns {Object} The normalized credential object
|
||||
*/
|
||||
User.normalizeCredentials = function(credentials, realmRequired, realmDelimiter) {
|
||||
var query = {};
|
||||
const query = {};
|
||||
credentials = credentials || {};
|
||||
if (!realmRequired) {
|
||||
if (credentials.email) {
|
||||
|
@ -162,7 +162,7 @@ module.exports = function(User) {
|
|||
if (credentials.realm) {
|
||||
query.realm = credentials.realm;
|
||||
}
|
||||
var parts;
|
||||
let parts;
|
||||
if (credentials.email) {
|
||||
parts = splitPrincipal(credentials.email, realmDelimiter);
|
||||
query.email = parts[1];
|
||||
|
@ -205,7 +205,7 @@ module.exports = function(User) {
|
|||
*/
|
||||
|
||||
User.login = function(credentials, include, fn) {
|
||||
var self = this;
|
||||
const self = this;
|
||||
if (typeof include === 'function') {
|
||||
fn = include;
|
||||
include = undefined;
|
||||
|
@ -222,33 +222,54 @@ module.exports = function(User) {
|
|||
include = include.toLowerCase();
|
||||
}
|
||||
|
||||
var realmDelimiter;
|
||||
let realmDelimiter;
|
||||
// Check if realm is required
|
||||
var realmRequired = !!(self.settings.realmRequired ||
|
||||
const realmRequired = !!(self.settings.realmRequired ||
|
||||
self.settings.realmDelimiter);
|
||||
if (realmRequired) {
|
||||
realmDelimiter = self.settings.realmDelimiter;
|
||||
}
|
||||
var query = self.normalizeCredentials(credentials, realmRequired,
|
||||
const query = self.normalizeCredentials(credentials, realmRequired,
|
||||
realmDelimiter);
|
||||
|
||||
if (realmRequired && !query.realm) {
|
||||
var err1 = new Error(g.f('{{realm}} is required'));
|
||||
err1.statusCode = 400;
|
||||
err1.code = 'REALM_REQUIRED';
|
||||
fn(err1);
|
||||
return fn.promise;
|
||||
if (realmRequired) {
|
||||
if (!query.realm) {
|
||||
const err1 = new Error(g.f('{{realm}} is required'));
|
||||
err1.statusCode = 400;
|
||||
err1.code = 'REALM_REQUIRED';
|
||||
fn(err1);
|
||||
return fn.promise;
|
||||
} else if (typeof query.realm !== 'string') {
|
||||
const err5 = new Error(g.f('Invalid realm'));
|
||||
err5.statusCode = 400;
|
||||
err5.code = 'INVALID_REALM';
|
||||
fn(err5);
|
||||
return fn.promise;
|
||||
}
|
||||
}
|
||||
if (!query.email && !query.username) {
|
||||
var err2 = new Error(g.f('{{username}} or {{email}} is required'));
|
||||
const err2 = new Error(g.f('{{username}} or {{email}} is required'));
|
||||
err2.statusCode = 400;
|
||||
err2.code = 'USERNAME_EMAIL_REQUIRED';
|
||||
fn(err2);
|
||||
return fn.promise;
|
||||
}
|
||||
if (query.username && typeof query.username !== 'string') {
|
||||
const err3 = new Error(g.f('Invalid username'));
|
||||
err3.statusCode = 400;
|
||||
err3.code = 'INVALID_USERNAME';
|
||||
fn(err3);
|
||||
return fn.promise;
|
||||
} else if (query.email && typeof query.email !== 'string') {
|
||||
const err4 = new Error(g.f('Invalid email'));
|
||||
err4.statusCode = 400;
|
||||
err4.code = 'INVALID_EMAIL';
|
||||
fn(err4);
|
||||
return fn.promise;
|
||||
}
|
||||
|
||||
self.findOne({where: query}, function(err, user) {
|
||||
var defaultError = new Error(g.f('login failed'));
|
||||
const defaultError = new Error(g.f('login failed'));
|
||||
defaultError.statusCode = 401;
|
||||
defaultError.code = 'LOGIN_FAILED';
|
||||
|
||||
|
@ -323,10 +344,10 @@ module.exports = function(User) {
|
|||
User.logout = function(tokenId, fn) {
|
||||
fn = fn || utils.createPromiseCallback();
|
||||
|
||||
var err;
|
||||
let err;
|
||||
if (!tokenId) {
|
||||
err = new Error(g.f('{{accessToken}} is required to logout'));
|
||||
err.status = 401;
|
||||
err.statusCode = 401;
|
||||
process.nextTick(fn, err);
|
||||
return fn.promise;
|
||||
}
|
||||
|
@ -336,7 +357,7 @@ module.exports = function(User) {
|
|||
fn(err);
|
||||
} else if ('count' in info && info.count === 0) {
|
||||
err = new Error(g.f('Could not find {{accessToken}}'));
|
||||
err.status = 401;
|
||||
err.statusCode = 401;
|
||||
fn(err);
|
||||
} else {
|
||||
fn();
|
||||
|
@ -346,12 +367,15 @@ module.exports = function(User) {
|
|||
};
|
||||
|
||||
User.observe('before delete', function(ctx, next) {
|
||||
var AccessToken = ctx.Model.relations.accessTokens.modelTo;
|
||||
var pkName = ctx.Model.definition.idName() || 'id';
|
||||
// Do nothing when the access control was disabled for this user model.
|
||||
if (!ctx.Model.relations.accessTokens) return next();
|
||||
|
||||
const AccessToken = ctx.Model.relations.accessTokens.modelTo;
|
||||
const pkName = ctx.Model.definition.idName() || 'id';
|
||||
ctx.Model.find({where: ctx.where, fields: [pkName]}, function(err, list) {
|
||||
if (err) return next(err);
|
||||
|
||||
var ids = list.map(function(u) { return u[pkName]; });
|
||||
const ids = list.map(function(u) { return u[pkName]; });
|
||||
ctx.where = {};
|
||||
ctx.where[pkName] = {inq: ids};
|
||||
|
||||
|
@ -603,11 +627,11 @@ module.exports = function(User) {
|
|||
*/
|
||||
|
||||
User.getVerifyOptions = function() {
|
||||
const verifyOptions = {
|
||||
const defaultOptions = {
|
||||
type: 'email',
|
||||
from: 'noreply@example.com',
|
||||
};
|
||||
return this.settings.verifyOptions || verifyOptions;
|
||||
return Object.assign({}, this.settings.verifyOptions || defaultOptions);
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -696,14 +720,18 @@ module.exports = function(User) {
|
|||
}
|
||||
cb = cb || utils.createPromiseCallback();
|
||||
|
||||
var user = this;
|
||||
var userModel = this.constructor;
|
||||
var registry = userModel.registry;
|
||||
|
||||
const user = this;
|
||||
const userModel = this.constructor;
|
||||
const registry = userModel.registry;
|
||||
verifyOptions = Object.assign({}, verifyOptions);
|
||||
// final assertion is performed once all options are assigned
|
||||
assert(typeof verifyOptions === 'object',
|
||||
'verifyOptions object param required when calling user.verify()');
|
||||
|
||||
// Shallow-clone the options object so that we don't override
|
||||
// the global default options object
|
||||
verifyOptions = Object.assign({}, verifyOptions);
|
||||
|
||||
// Set a default template generation function if none provided
|
||||
verifyOptions.templateFn = verifyOptions.templateFn || createVerificationEmailBody;
|
||||
|
||||
|
@ -715,39 +743,50 @@ module.exports = function(User) {
|
|||
verifyOptions.mailer = verifyOptions.mailer || userModel.email ||
|
||||
registry.getModelByType(loopback.Email);
|
||||
|
||||
var pkName = userModel.definition.idName() || 'id';
|
||||
const pkName = userModel.definition.idName() || 'id';
|
||||
verifyOptions.redirect = verifyOptions.redirect || '/';
|
||||
var defaultTemplate = path.join(__dirname, '..', '..', 'templates', 'verify.ejs');
|
||||
const defaultTemplate = path.join(__dirname, '..', '..', 'templates', 'verify.ejs');
|
||||
verifyOptions.template = path.resolve(verifyOptions.template || defaultTemplate);
|
||||
verifyOptions.user = user;
|
||||
verifyOptions.protocol = verifyOptions.protocol || 'http';
|
||||
|
||||
var app = userModel.app;
|
||||
const app = userModel.app;
|
||||
verifyOptions.host = verifyOptions.host || (app && app.get('host')) || 'localhost';
|
||||
verifyOptions.port = verifyOptions.port || (app && app.get('port')) || 3000;
|
||||
verifyOptions.restApiRoot = verifyOptions.restApiRoot || (app && app.get('restApiRoot')) || '/api';
|
||||
|
||||
var displayPort = (
|
||||
const displayPort = (
|
||||
(verifyOptions.protocol === 'http' && verifyOptions.port == '80') ||
|
||||
(verifyOptions.protocol === 'https' && verifyOptions.port == '443')
|
||||
) ? '' : ':' + verifyOptions.port;
|
||||
|
||||
var urlPath = joinUrlPath(
|
||||
verifyOptions.restApiRoot,
|
||||
userModel.http.path,
|
||||
userModel.sharedClass.findMethodByName('confirm').http.path
|
||||
);
|
||||
if (!verifyOptions.verifyHref) {
|
||||
const confirmMethod = userModel.sharedClass.findMethodByName('confirm');
|
||||
if (!confirmMethod) {
|
||||
throw new Error(
|
||||
'Cannot build user verification URL, ' +
|
||||
'the default confirm method is not public. ' +
|
||||
'Please provide the URL in verifyOptions.verifyHref.',
|
||||
);
|
||||
}
|
||||
|
||||
verifyOptions.verifyHref = verifyOptions.verifyHref ||
|
||||
verifyOptions.protocol +
|
||||
'://' +
|
||||
verifyOptions.host +
|
||||
displayPort +
|
||||
urlPath +
|
||||
'?' + qs.stringify({
|
||||
uid: '' + verifyOptions.user[pkName],
|
||||
redirect: verifyOptions.redirect,
|
||||
});
|
||||
const urlPath = joinUrlPath(
|
||||
verifyOptions.restApiRoot,
|
||||
userModel.http.path,
|
||||
confirmMethod.http.path,
|
||||
);
|
||||
|
||||
verifyOptions.verifyHref =
|
||||
verifyOptions.protocol +
|
||||
'://' +
|
||||
verifyOptions.host +
|
||||
displayPort +
|
||||
urlPath +
|
||||
'?' + qs.stringify({
|
||||
uid: '' + verifyOptions.user[pkName],
|
||||
redirect: verifyOptions.redirect,
|
||||
});
|
||||
}
|
||||
|
||||
verifyOptions.to = verifyOptions.to || user.email;
|
||||
verifyOptions.subject = verifyOptions.subject || g.f('Thanks for Registering');
|
||||
|
@ -757,7 +796,7 @@ module.exports = function(User) {
|
|||
assertVerifyOptions(verifyOptions);
|
||||
|
||||
// argument "options" is passed depending on verifyOptions.generateVerificationToken function requirements
|
||||
var tokenGenerator = verifyOptions.generateVerificationToken;
|
||||
const tokenGenerator = verifyOptions.generateVerificationToken;
|
||||
if (tokenGenerator.length == 3) {
|
||||
tokenGenerator(user, options, addTokenToUserAndSave);
|
||||
} else {
|
||||
|
@ -775,14 +814,17 @@ module.exports = function(User) {
|
|||
|
||||
// TODO - support more verification types
|
||||
function sendEmail(user) {
|
||||
verifyOptions.verifyHref += '&token=' + user.verificationToken;
|
||||
verifyOptions.verifyHref +=
|
||||
verifyOptions.verifyHref.indexOf('?') === -1 ? '?' : '&';
|
||||
verifyOptions.verifyHref += 'token=' + user.verificationToken;
|
||||
|
||||
verifyOptions.verificationToken = user.verificationToken;
|
||||
verifyOptions.text = verifyOptions.text || g.f('Please verify your email by opening ' +
|
||||
'this link in a web browser:\n\t%s', verifyOptions.verifyHref);
|
||||
verifyOptions.text = verifyOptions.text.replace(/\{href\}/g, verifyOptions.verifyHref);
|
||||
|
||||
// argument "options" is passed depending on templateFn function requirements
|
||||
var templateFn = verifyOptions.templateFn;
|
||||
const templateFn = verifyOptions.templateFn;
|
||||
if (templateFn.length == 3) {
|
||||
templateFn(verifyOptions, options, setHtmlContentAndSend);
|
||||
} else {
|
||||
|
@ -799,7 +841,7 @@ module.exports = function(User) {
|
|||
delete verifyOptions.template;
|
||||
|
||||
// argument "options" is passed depending on Email.send function requirements
|
||||
var Email = verifyOptions.mailer;
|
||||
const Email = verifyOptions.mailer;
|
||||
if (Email.send.length == 3) {
|
||||
Email.send(verifyOptions, options, handleAfterSend);
|
||||
} else {
|
||||
|
@ -831,8 +873,8 @@ module.exports = function(User) {
|
|||
}
|
||||
|
||||
function createVerificationEmailBody(verifyOptions, options, cb) {
|
||||
var template = loopback.template(verifyOptions.template);
|
||||
var body = template(verifyOptions);
|
||||
const template = loopback.template(verifyOptions.template);
|
||||
const body = template(verifyOptions);
|
||||
cb(null, body);
|
||||
}
|
||||
|
||||
|
@ -910,11 +952,11 @@ module.exports = function(User) {
|
|||
|
||||
User.resetPassword = function(options, cb) {
|
||||
cb = cb || utils.createPromiseCallback();
|
||||
var UserModel = this;
|
||||
var ttl = UserModel.settings.resetPasswordTokenTTL || DEFAULT_RESET_PW_TTL;
|
||||
const UserModel = this;
|
||||
const ttl = UserModel.settings.resetPasswordTokenTTL || DEFAULT_RESET_PW_TTL;
|
||||
options = options || {};
|
||||
if (typeof options.email !== 'string') {
|
||||
var err = new Error(g.f('Email is required'));
|
||||
const err = new Error(g.f('Email is required'));
|
||||
err.statusCode = 400;
|
||||
err.code = 'EMAIL_REQUIRED';
|
||||
cb(err);
|
||||
|
@ -928,7 +970,7 @@ module.exports = function(User) {
|
|||
} catch (err) {
|
||||
return cb(err);
|
||||
}
|
||||
var where = {
|
||||
const where = {
|
||||
email: options.email,
|
||||
};
|
||||
if (options.realm) {
|
||||
|
@ -989,12 +1031,12 @@ module.exports = function(User) {
|
|||
*/
|
||||
User.hashPassword = function(plain) {
|
||||
this.validatePassword(plain);
|
||||
var salt = bcrypt.genSaltSync(this.settings.saltWorkFactor || SALT_WORK_FACTOR);
|
||||
const salt = bcrypt.genSaltSync(this.settings.saltWorkFactor || SALT_WORK_FACTOR);
|
||||
return bcrypt.hashSync(plain, salt);
|
||||
};
|
||||
|
||||
User.validatePassword = function(plain) {
|
||||
var err;
|
||||
let err;
|
||||
if (!plain || typeof plain !== 'string') {
|
||||
err = new Error(g.f('Invalid password.'));
|
||||
err.code = 'INVALID_PASSWORD';
|
||||
|
@ -1003,7 +1045,7 @@ module.exports = function(User) {
|
|||
}
|
||||
|
||||
// Bcrypt only supports up to 72 bytes; the rest is silently dropped.
|
||||
var len = Buffer.byteLength(plain, 'utf8');
|
||||
const len = Buffer.byteLength(plain, 'utf8');
|
||||
if (len > MAX_PASSWORD_LENGTH) {
|
||||
err = new Error(g.f('The password entered was too long. Max length is %d (entered %d)',
|
||||
MAX_PASSWORD_LENGTH, len));
|
||||
|
@ -1022,20 +1064,20 @@ module.exports = function(User) {
|
|||
if (!Array.isArray(userIds) || !userIds.length)
|
||||
return process.nextTick(cb);
|
||||
|
||||
var accessTokenRelation = this.relations.accessTokens;
|
||||
const accessTokenRelation = this.relations.accessTokens;
|
||||
if (!accessTokenRelation)
|
||||
return process.nextTick(cb);
|
||||
|
||||
var AccessToken = accessTokenRelation.modelTo;
|
||||
var query = {userId: {inq: userIds}};
|
||||
var tokenPK = AccessToken.definition.idName() || 'id';
|
||||
const AccessToken = accessTokenRelation.modelTo;
|
||||
const query = {userId: {inq: userIds}};
|
||||
const tokenPK = AccessToken.definition.idName() || 'id';
|
||||
if (options.accessToken && tokenPK in options.accessToken) {
|
||||
query[tokenPK] = {neq: options.accessToken[tokenPK]};
|
||||
}
|
||||
// add principalType in AccessToken.query if using polymorphic relations
|
||||
// between AccessToken and User
|
||||
var relatedUser = AccessToken.relations.user;
|
||||
var isRelationPolymorphic = relatedUser && relatedUser.polymorphic &&
|
||||
const relatedUser = AccessToken.relations.user;
|
||||
const isRelationPolymorphic = relatedUser && relatedUser.polymorphic &&
|
||||
!relatedUser.modelTo;
|
||||
if (isRelationPolymorphic) {
|
||||
query.principalType = this.modelName;
|
||||
|
@ -1050,14 +1092,14 @@ module.exports = function(User) {
|
|||
User.setup = function() {
|
||||
// We need to call the base class's setup method
|
||||
User.base.setup.call(this);
|
||||
var UserModel = this;
|
||||
const UserModel = this;
|
||||
|
||||
// max ttl
|
||||
this.settings.maxTTL = this.settings.maxTTL || DEFAULT_MAX_TTL;
|
||||
this.settings.ttl = this.settings.ttl || DEFAULT_TTL;
|
||||
|
||||
UserModel.setter.email = function(value) {
|
||||
if (!UserModel.settings.caseSensitiveEmail) {
|
||||
if (!UserModel.settings.caseSensitiveEmail && typeof value === 'string') {
|
||||
this.$email = value.toLowerCase();
|
||||
} else {
|
||||
this.$email = value;
|
||||
|
@ -1068,7 +1110,7 @@ module.exports = function(User) {
|
|||
if (typeof plain !== 'string') {
|
||||
return;
|
||||
}
|
||||
if (plain.indexOf('$2a$') === 0 && plain.length === 60) {
|
||||
if ((plain.indexOf('$2a$') === 0 || plain.indexOf('$2b$') === 0) && plain.length === 60) {
|
||||
// The password is already hashed. It can be the case
|
||||
// when the instance is loaded from DB
|
||||
this.$password = plain;
|
||||
|
@ -1079,7 +1121,7 @@ module.exports = function(User) {
|
|||
|
||||
// Make sure emailVerified is not set by creation
|
||||
UserModel.beforeRemote('create', function(ctx, user, next) {
|
||||
var body = ctx.req.body;
|
||||
const body = ctx.req.body;
|
||||
if (body && body.emailVerified) {
|
||||
body.emailVerified = false;
|
||||
}
|
||||
|
@ -1106,7 +1148,7 @@ module.exports = function(User) {
|
|||
'{{(`include=user`)}}\n\n'),
|
||||
},
|
||||
http: {verb: 'post'},
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
UserModel.remoteMethod(
|
||||
|
@ -1115,9 +1157,9 @@ module.exports = function(User) {
|
|||
description: 'Logout a user with access token.',
|
||||
accepts: [
|
||||
{arg: 'access_token', type: 'string', http: function(ctx) {
|
||||
var req = ctx && ctx.req;
|
||||
var accessToken = req && req.accessToken;
|
||||
var tokenID = accessToken ? accessToken.id : undefined;
|
||||
const req = ctx && ctx.req;
|
||||
const accessToken = req && req.accessToken;
|
||||
const tokenID = accessToken ? accessToken.id : undefined;
|
||||
|
||||
return tokenID;
|
||||
}, description: 'Do not supply this argument, it is automatically extracted ' +
|
||||
|
@ -1125,7 +1167,7 @@ module.exports = function(User) {
|
|||
},
|
||||
],
|
||||
http: {verb: 'all'},
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
UserModel.remoteMethod(
|
||||
|
@ -1137,7 +1179,7 @@ module.exports = function(User) {
|
|||
{arg: 'options', type: 'object', http: 'optionsFromRequest'},
|
||||
],
|
||||
http: {verb: 'post'},
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
UserModel.remoteMethod(
|
||||
|
@ -1150,7 +1192,7 @@ module.exports = function(User) {
|
|||
{arg: 'redirect', type: 'string'},
|
||||
],
|
||||
http: {verb: 'get', path: '/confirm'},
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
UserModel.remoteMethod(
|
||||
|
@ -1161,7 +1203,7 @@ module.exports = function(User) {
|
|||
{arg: 'options', type: 'object', required: true, http: {source: 'body'}},
|
||||
],
|
||||
http: {verb: 'post', path: '/reset'},
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
UserModel.remoteMethod(
|
||||
|
@ -1169,15 +1211,13 @@ module.exports = function(User) {
|
|||
{
|
||||
description: 'Change a user\'s password.',
|
||||
accepts: [
|
||||
{arg: 'id', type: 'any',
|
||||
http: ctx => ctx.req.accessToken && ctx.req.accessToken.userId,
|
||||
},
|
||||
{arg: 'id', type: 'any', http: getUserIdFromRequestContext},
|
||||
{arg: 'oldPassword', type: 'string', required: true, http: {source: 'form'}},
|
||||
{arg: 'newPassword', type: 'string', required: true, http: {source: 'form'}},
|
||||
{arg: 'options', type: 'object', http: 'optionsFromRequest'},
|
||||
],
|
||||
http: {verb: 'POST', path: '/change-password'},
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
const setPasswordScopes = UserModel.settings.restrictResetPasswordTokenScope ?
|
||||
|
@ -1188,17 +1228,32 @@ module.exports = function(User) {
|
|||
{
|
||||
description: 'Reset user\'s password via a password-reset token.',
|
||||
accepts: [
|
||||
{arg: 'id', type: 'any',
|
||||
http: ctx => ctx.req.accessToken && ctx.req.accessToken.userId,
|
||||
},
|
||||
{arg: 'id', type: 'any', http: getUserIdFromRequestContext},
|
||||
{arg: 'newPassword', type: 'string', required: true, http: {source: 'form'}},
|
||||
{arg: 'options', type: 'object', http: 'optionsFromRequest'},
|
||||
],
|
||||
accessScopes: setPasswordScopes,
|
||||
http: {verb: 'POST', path: '/reset-password'},
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
function getUserIdFromRequestContext(ctx) {
|
||||
const token = ctx.req.accessToken;
|
||||
if (!token) return;
|
||||
|
||||
const hasPrincipalType = 'principalType' in token;
|
||||
if (hasPrincipalType && token.principalType !== UserModel.modelName) {
|
||||
// We have multiple user models related to the same access token model
|
||||
// and the token used to authorize reset-password request was created
|
||||
// for a different user model.
|
||||
const err = new Error(g.f('Access Denied'));
|
||||
err.statusCode = 403;
|
||||
throw err;
|
||||
}
|
||||
|
||||
return token.userId;
|
||||
}
|
||||
|
||||
UserModel.afterRemote('confirm', function(ctx, inst, next) {
|
||||
if (ctx.args.redirect !== undefined) {
|
||||
if (!ctx.res) {
|
||||
|
@ -1282,7 +1337,8 @@ module.exports = function(User) {
|
|||
// This is a programmer's error, use the default status code 500
|
||||
return next(new Error(
|
||||
'Invalid use of "options.setPassword". Only "password" can be ' +
|
||||
'changed when using this option.'));
|
||||
'changed when using this option.',
|
||||
));
|
||||
}
|
||||
|
||||
return next();
|
||||
|
@ -1294,7 +1350,8 @@ module.exports = function(User) {
|
|||
|
||||
const err = new Error(
|
||||
'Changing user password via patch/replace API is not allowed. ' +
|
||||
'Use changePassword() or setPassword() instead.');
|
||||
'Use changePassword() or setPassword() instead.',
|
||||
);
|
||||
err.statusCode = 401;
|
||||
err.code = 'PASSWORD_CHANGE_NOT_ALLOWED';
|
||||
next(err);
|
||||
|
@ -1304,8 +1361,8 @@ module.exports = function(User) {
|
|||
if (ctx.isNewInstance) return next();
|
||||
if (!ctx.where && !ctx.instance) return next();
|
||||
|
||||
var pkName = ctx.Model.definition.idName() || 'id';
|
||||
var where = ctx.where;
|
||||
const pkName = ctx.Model.definition.idName() || 'id';
|
||||
let where = ctx.where;
|
||||
if (!where) {
|
||||
where = {};
|
||||
where[pkName] = ctx.instance[pkName];
|
||||
|
@ -1314,15 +1371,22 @@ module.exports = function(User) {
|
|||
ctx.Model.find({where: where}, ctx.options, function(err, userInstances) {
|
||||
if (err) return next(err);
|
||||
ctx.hookState.originalUserData = userInstances.map(function(u) {
|
||||
var user = {};
|
||||
const user = {};
|
||||
user[pkName] = u[pkName];
|
||||
user.email = u.email;
|
||||
user.password = u.password;
|
||||
return user;
|
||||
});
|
||||
var emailChanged;
|
||||
let emailChanged;
|
||||
if (ctx.instance) {
|
||||
emailChanged = ctx.instance.email !== ctx.hookState.originalUserData[0].email;
|
||||
// Check if map does not return an empty array
|
||||
// Fix server crashes when try to PUT a non existent id
|
||||
if (ctx.hookState.originalUserData.length > 0) {
|
||||
emailChanged = ctx.instance.email !== ctx.hookState.originalUserData[0].email;
|
||||
} else {
|
||||
emailChanged = true;
|
||||
}
|
||||
|
||||
if (emailChanged && ctx.Model.settings.emailVerificationRequired) {
|
||||
ctx.instance.emailVerified = false;
|
||||
}
|
||||
|
@ -1343,13 +1407,15 @@ module.exports = function(User) {
|
|||
if (!ctx.instance && !ctx.data) return next();
|
||||
if (!ctx.hookState.originalUserData) return next();
|
||||
|
||||
var pkName = ctx.Model.definition.idName() || 'id';
|
||||
var newEmail = (ctx.instance || ctx.data).email;
|
||||
var newPassword = (ctx.instance || ctx.data).password;
|
||||
const pkName = ctx.Model.definition.idName() || 'id';
|
||||
const newEmail = (ctx.instance || ctx.data).email;
|
||||
const newPassword = (ctx.instance || ctx.data).password;
|
||||
|
||||
if (!newEmail && !newPassword) return next();
|
||||
|
||||
var userIdsToExpire = ctx.hookState.originalUserData.filter(function(u) {
|
||||
if (ctx.options.preserveAccessTokens) return next();
|
||||
|
||||
const userIdsToExpire = ctx.hookState.originalUserData.filter(function(u) {
|
||||
return (newEmail && u.email !== newEmail) ||
|
||||
(newPassword && u.password !== newPassword);
|
||||
}).map(function(u) {
|
||||
|
@ -1360,7 +1426,7 @@ module.exports = function(User) {
|
|||
};
|
||||
|
||||
function emailValidator(err, done) {
|
||||
var value = this.email;
|
||||
const value = this.email;
|
||||
if (value == null)
|
||||
return;
|
||||
if (typeof value !== 'string')
|
||||
|
@ -1371,9 +1437,9 @@ function emailValidator(err, done) {
|
|||
}
|
||||
|
||||
function joinUrlPath(args) {
|
||||
var result = arguments[0];
|
||||
for (var ix = 1; ix < arguments.length; ix++) {
|
||||
var next = arguments[ix];
|
||||
let result = arguments[0];
|
||||
for (let ix = 1; ix < arguments.length; ix++) {
|
||||
const next = arguments[ix];
|
||||
result += result[result.length - 1] === '/' && next[0] === '/' ?
|
||||
next.slice(1) : next;
|
||||
}
|
||||
|
|
|
@ -1,14 +1,14 @@
|
|||
// Copyright IBM Corp. 2014,2016. All Rights Reserved.
|
||||
// Copyright IBM Corp. 2014,2019. All Rights Reserved.
|
||||
// Node module: loopback
|
||||
// This file is licensed under the MIT License.
|
||||
// License text available at https://opensource.org/licenses/MIT
|
||||
|
||||
'use strict';
|
||||
var g = require('../../lib/globalize');
|
||||
var loopback = require('../../');
|
||||
var client = loopback();
|
||||
var CartItem = require('./models').CartItem;
|
||||
var remote = loopback.createDataSource({
|
||||
const g = require('../../lib/globalize');
|
||||
const loopback = require('../../');
|
||||
const client = loopback();
|
||||
const CartItem = require('./models').CartItem;
|
||||
const remote = loopback.createDataSource({
|
||||
connector: loopback.Remote,
|
||||
url: 'http://localhost:3000',
|
||||
});
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
// Copyright IBM Corp. 2014,2016. All Rights Reserved.
|
||||
// Copyright IBM Corp. 2014,2019. All Rights Reserved.
|
||||
// Node module: loopback
|
||||
// This file is licensed under the MIT License.
|
||||
// License text available at https://opensource.org/licenses/MIT
|
||||
|
||||
'use strict';
|
||||
var loopback = require('../../');
|
||||
const loopback = require('../../');
|
||||
|
||||
var CartItem = exports.CartItem = loopback.PersistedModel.extend('CartItem', {
|
||||
const CartItem = exports.CartItem = loopback.PersistedModel.extend('CartItem', {
|
||||
tax: {type: Number, default: 0.1},
|
||||
price: Number,
|
||||
item: String,
|
||||
|
@ -17,7 +17,7 @@ var CartItem = exports.CartItem = loopback.PersistedModel.extend('CartItem', {
|
|||
CartItem.sum = function(cartId, callback) {
|
||||
this.find({where: {cartId: cartId}}, function(err, items) {
|
||||
if (err) return callback(err);
|
||||
var total = items
|
||||
const total = items
|
||||
.map(function(item) {
|
||||
return item.total();
|
||||
})
|
||||
|
@ -33,8 +33,7 @@ CartItem.remoteMethod('sum',
|
|||
{
|
||||
accepts: {arg: 'cartId', type: 'number'},
|
||||
returns: {arg: 'total', type: 'number'},
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
CartItem.prototype.total = function() {
|
||||
return this.price * this.qty * (1 + this.tax);
|
||||
|
|
|
@ -1,13 +1,13 @@
|
|||
// Copyright IBM Corp. 2014,2016. All Rights Reserved.
|
||||
// Copyright IBM Corp. 2014,2019. All Rights Reserved.
|
||||
// Node module: loopback
|
||||
// This file is licensed under the MIT License.
|
||||
// License text available at https://opensource.org/licenses/MIT
|
||||
|
||||
'use strict';
|
||||
var loopback = require('../../');
|
||||
var server = module.exports = loopback();
|
||||
var CartItem = require('./models').CartItem;
|
||||
var memory = loopback.createDataSource({
|
||||
const loopback = require('../../');
|
||||
const server = module.exports = loopback();
|
||||
const CartItem = require('./models').CartItem;
|
||||
const memory = loopback.createDataSource({
|
||||
connector: loopback.Memory,
|
||||
});
|
||||
|
||||
|
|
|
@ -1,21 +1,21 @@
|
|||
// Copyright IBM Corp. 2013,2016. All Rights Reserved.
|
||||
// Copyright IBM Corp. 2013,2019. All Rights Reserved.
|
||||
// Node module: loopback
|
||||
// This file is licensed under the MIT License.
|
||||
// License text available at https://opensource.org/licenses/MIT
|
||||
|
||||
'use strict';
|
||||
var g = require('../../lib/globalize');
|
||||
var loopback = require('../../');
|
||||
var app = loopback();
|
||||
const g = require('../../lib/globalize');
|
||||
const loopback = require('../../');
|
||||
const app = loopback();
|
||||
|
||||
app.use(loopback.rest());
|
||||
|
||||
var schema = {
|
||||
const schema = {
|
||||
name: String,
|
||||
};
|
||||
|
||||
app.dataSource('db', {connector: 'memory'});
|
||||
var Color = app.registry.createModel('color', schema);
|
||||
const Color = app.registry.createModel('color', schema);
|
||||
app.model(Color, {dataSource: 'db'});
|
||||
|
||||
Color.create({name: 'red'});
|
||||
|
|
|
@ -1,23 +1,23 @@
|
|||
// Copyright IBM Corp. 2013,2016. All Rights Reserved.
|
||||
// Copyright IBM Corp. 2013,2019. All Rights Reserved.
|
||||
// Node module: loopback
|
||||
// This file is licensed under the MIT License.
|
||||
// License text available at https://opensource.org/licenses/MIT
|
||||
|
||||
'use strict';
|
||||
var g = require('../../lib/globalize');
|
||||
var models = require('../../lib/models');
|
||||
var loopback = require('../../');
|
||||
var app = loopback();
|
||||
const g = require('../../lib/globalize');
|
||||
const models = require('../../lib/models');
|
||||
const loopback = require('../../');
|
||||
const app = loopback();
|
||||
|
||||
app.use(loopback.rest());
|
||||
|
||||
var dataSource = loopback.createDataSource('db', {connector: loopback.Memory});
|
||||
const dataSource = loopback.createDataSource('db', {connector: loopback.Memory});
|
||||
|
||||
var Application = models.Application(dataSource);
|
||||
const Application = models.Application(dataSource);
|
||||
|
||||
app.model(Application);
|
||||
|
||||
var data = {
|
||||
const data = {
|
||||
pushSettings: [{
|
||||
'platform': 'apns',
|
||||
'apns': {
|
||||
|
@ -43,10 +43,10 @@ Application.create(data, function(err, data) {
|
|||
});
|
||||
|
||||
Application.register('rfeng', 'MyApp', {description: g.f('My first mobile application')},
|
||||
function(err, result) {
|
||||
console.log(result.toObject());
|
||||
|
||||
result.resetKeys(function(err, result) {
|
||||
function(err, result) {
|
||||
console.log(result.toObject());
|
||||
|
||||
result.resetKeys(function(err, result) {
|
||||
console.log(result.toObject());
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -1,22 +1,22 @@
|
|||
// Copyright IBM Corp. 2014,2016. All Rights Reserved.
|
||||
// Copyright IBM Corp. 2014,2019. All Rights Reserved.
|
||||
// Node module: loopback
|
||||
// This file is licensed under the MIT License.
|
||||
// License text available at https://opensource.org/licenses/MIT
|
||||
|
||||
'use strict';
|
||||
var loopback = require('../../');
|
||||
var app = loopback();
|
||||
var db = app.dataSource('db', {connector: 'memory'});
|
||||
var Color = app.registry.createModel('color', {}, {trackChanges: true});
|
||||
const loopback = require('../../');
|
||||
const app = loopback();
|
||||
const db = app.dataSource('db', {connector: 'memory'});
|
||||
const Color = app.registry.createModel('color', {}, {trackChanges: true});
|
||||
app.model(Color, {dataSource: 'db'});
|
||||
var Color2 = app.registry.createModel('color2', {}, {trackChanges: true});
|
||||
const Color2 = app.registry.createModel('color2', {}, {trackChanges: true});
|
||||
app.model(Color2, {dataSource: 'db'});
|
||||
var target = Color2;
|
||||
var source = Color;
|
||||
var SPEED = process.env.SPEED || 100;
|
||||
var conflicts;
|
||||
const target = Color2;
|
||||
const source = Color;
|
||||
const SPEED = process.env.SPEED || 100;
|
||||
let conflicts;
|
||||
|
||||
var steps = [
|
||||
const steps = [
|
||||
|
||||
createSomeInitialSourceData,
|
||||
|
||||
|
@ -137,7 +137,7 @@ function list(model, msg) {
|
|||
|
||||
function run(steps) {
|
||||
setInterval(function() {
|
||||
var step = steps.shift();
|
||||
const step = steps.shift();
|
||||
if (step) {
|
||||
console.log(step.name);
|
||||
step();
|
||||
|
|
|
@ -1,18 +1,18 @@
|
|||
// Copyright IBM Corp. 2013,2016. All Rights Reserved.
|
||||
// Copyright IBM Corp. 2013,2019. All Rights Reserved.
|
||||
// Node module: loopback
|
||||
// This file is licensed under the MIT License.
|
||||
// License text available at https://opensource.org/licenses/MIT
|
||||
|
||||
'use strict';
|
||||
var g = require('../../lib/globalize');
|
||||
var loopback = require('../../');
|
||||
var app = loopback();
|
||||
const g = require('../../lib/globalize');
|
||||
const loopback = require('../../');
|
||||
const app = loopback();
|
||||
|
||||
app.use(loopback.rest());
|
||||
|
||||
var dataSource = app.dataSource('db', {adapter: 'memory'});
|
||||
const dataSource = app.dataSource('db', {adapter: 'memory'});
|
||||
|
||||
var Color = dataSource.define('color', {
|
||||
const Color = dataSource.define('color', {
|
||||
'name': String,
|
||||
});
|
||||
|
||||
|
|
3
index.js
3
index.js
|
@ -1,4 +1,4 @@
|
|||
// Copyright IBM Corp. 2013,2016. All Rights Reserved.
|
||||
// Copyright IBM Corp. 2013,2018. All Rights Reserved.
|
||||
// Node module: loopback
|
||||
// This file is licensed under the MIT License.
|
||||
// License text available at https://opensource.org/licenses/MIT
|
||||
|
@ -25,4 +25,5 @@ loopback.Remote = require('loopback-connector-remote');
|
|||
*/
|
||||
|
||||
loopback.GeoPoint = require('loopback-datasource-juggler/lib/geo').GeoPoint;
|
||||
loopback.DateString = require('loopback-datasource-juggler/lib/date-string');
|
||||
loopback.ValidationError = loopback.Model.ValidationError;
|
||||
|
|
|
@ -0,0 +1,93 @@
|
|||
{
|
||||
"03f79fa268fe199de2ce4345515431c1": "Nebyl nalezen žádný záznam změny pro {0} s ID {1}",
|
||||
"04bd8af876f001ceaf443aad6a9002f9": "Ověření vyžaduje, aby byl definován model {0}.",
|
||||
"095afbf2f1f0e5be678f5dac5c54e717": "Přístup odepřen",
|
||||
"0caffe1d763c8cca6a61814abe33b776": "Je vyžadován e-mail.",
|
||||
"0da38687fed24275c1547e815914a8e3": "Odstraňte související položku podle ID pro {0}.",
|
||||
"0e21aad369dd09e1965c11949303cefd": "Metadata vzdálené komunikace pro {0}.{1} {{\"isStatic\"}} se neshodují s novým stylem založeném na názvu metody.",
|
||||
"10e01c895dc0b2fecc385f9f462f1ca6": "Seznam barev je dostupný na adrese {{http://localhost:3000/colors}}",
|
||||
"1b2a6076dccbe91a56f1672eb3b8598c": "Tělo odezvy obsahuje vlastnosti {{AccessToken}} vytvořené při přihlášení.\nV závislosti na hodnotě parametru `include` může tělo obsahovat další vlastnosti:\n\n - `user` - `U+007BUserU+007D` - Data aktuálně přihlášeného uživatele. {{(`include=user`)}}\n\n",
|
||||
"1d7833c3ca2f05fdad8fad7537531c40": "\t PŘEDMĚT:{0}",
|
||||
"1e85f822b547a75d7d385048030e4ecb": "VytvořenO: {0}",
|
||||
"22fe62fa8d595b72c62208beddaa2a56": "Aktualizujte související položku podle ID pro {0}.",
|
||||
"275f22ab95671f095640ca99194b7635": "\t OD: {0}",
|
||||
"2860bccdf9ef1e350c1a38932ed12173": "{0} byl odebrán ve verzi 3.0. Další podrobnosti naleznete v části {1}.",
|
||||
"2d3071e3b18681c80a090dc0efbdb349": "Nelze najít {0} s ID {1}",
|
||||
"316e5b82c203cf3de31a449ee07d0650": "Očekávaná logická hodnota, obdrženo: {0}",
|
||||
"320c482401afa1207c04343ab162e803": "Neplatný typ činitele: {0}",
|
||||
"3438fab56cc7ab92dfd88f0497e523e0": "Vlastnost vztahů konfigurace `{0}` musí být objekt",
|
||||
"3591f1d3e115b46f9f195df5ca548a6a": "Model nebyl nalezen: model `{0}` rozšiřuje neznámý model `{1}`.",
|
||||
"35e5252c62d80f8c54a5290d30f4c7d0": "Ověřte svůj e-mail otevřením tohoto odkazu ve webovém prohlížeči:\n\t {0}",
|
||||
"37bcd4b50dfae98734772e39ffb1ea3d": "Zadané heslo je příliš dlouhé. Maximální délka je {0} (zadáno {1})",
|
||||
"3aae63bb7e8e046641767571c1591441": "Přihlášení se nezdařilo, protože e-mail nebyl ověřen",
|
||||
"3aecb24fa8bdd3f79d168761ca8a6729": "Neznámá fáze {{middleware}} {0}",
|
||||
"3ca45aa6f705c46a4c598a900716f086": "{0} používá nastavení modelu {1}, které již není dostupné.",
|
||||
"3caaa84fc103d6d5612173ae6d43b245": "Neplatný token: {0}",
|
||||
"3d617953470be16d0c2b32f0bcfbb5ee": "Děkujeme za registraci",
|
||||
"3d63008ccfb2af1db2142e8cc2716ace": "Varování: Pro odeslání e-mailu nebyl zadán žádný přenos e-mailu. Nastavení přenosu pro odesílání poštovních zpráv.",
|
||||
"4203ab415ec66a78d3164345439ba76e": "Nelze volat {0}.{1}(). Metoda {2} nebyla nastavena. {{PersistedModel}} nebyl správně připojen ke {{DataSource}}!",
|
||||
"42a36bac5cf03c4418d664500c81047a": "Volba {{DataSource}} {{\"defaultForType\"}} se již nepodporuje",
|
||||
"44a6c8b1ded4ed653d19ddeaaf89a606": "E-mail nebyl nalezen",
|
||||
"4a4f04a4e480fc5d4ee73b84d9a4b904": "Odesílání pošty:",
|
||||
"4b494de07f524703ac0879addbd64b13": "E-mail nebyl ověřen",
|
||||
"528325f3cbf1b0ab9a08447515daac9a": "Aktualizujte {0} tohoto modelu.",
|
||||
"543d19bad5e47ee1e9eb8af688e857b4": "Cizí klíč pro {0}.",
|
||||
"57b87ae0e65f6ab7a2e3e6cbdfca49a4": "Nelze vytvořit zdroj dat {0}: {1}",
|
||||
"5858e63efaa0e4ad86b61c0459ea32fa": "Musíte připojit model {{Email}} ke konektoru {{Mail}}",
|
||||
"598ff0255ffd1d1b71e8de55dbe2c034": "Zkontrolujte existenci vztahu {0} k položce podle ID.",
|
||||
"5a36cc6ba0cc27c754f6c5ed6015ea3c": "Odeberte vztah {0} k položce podle ID.",
|
||||
"5e81ad3847a290dc650b47618b9cbc7e": "Přihlášení se nezdařilo",
|
||||
"5fa3afb425819ebde958043e598cb664": "Nelze najít model s {{id}} {0}",
|
||||
"61e5deebaf44d68f4e6a508f30cc31a3": "Vztah `{0}` neexistuje pro model `{1}`",
|
||||
"62e8b0a733417978bab22c8dacf5d7e6": "Nelze použít hromadné aktualizace, konektor správně nehlásí počet aktualizovaných záznamů.",
|
||||
"63a091ced88001ab6acb58f61ec041c5": "\t TEXT:{0}",
|
||||
"651f0b3cbba001635152ec3d3d954d0a": "Najdete související položku podle ID pro {0}.",
|
||||
"6bc376432cd9972cf991aad3de371e78": "Chybí data pro změnu: {0}",
|
||||
"705c2d456a3e204c4af56e671ec3225c": "Nebyl nalezen {{accessToken}}.",
|
||||
"734a7bebb65e10899935126ba63dd51f": "Vlastnost voleb konfigurace `{0}` musí být objekt",
|
||||
"779467f467862836e19f494a37d6ab77": "Vlastnost acls konfigurace `{0}` musí být pole objektů",
|
||||
"7bc7b301ad9c4fc873029d57fb9740fe": "Dotazy {0} z {1}.",
|
||||
"7c837b88fd0e509bd3fc722d7ddf0711": "Cizí klíč pro {0}",
|
||||
"7d5e7ed0efaedf3f55f380caae0df8b8": "Moje první mobilní aplikace",
|
||||
"7e0fca41d098607e1c9aa353c67e0fa1": "Neplatný přístupový token",
|
||||
"7e287fc885d9fdcf42da3a12f38572c1": "Vyžadována autorizace",
|
||||
"7ea04ea91aac3cb7ce0ddd96b7ff1fa4": "{{accessToken}} je vyžadován pro odhlášení",
|
||||
"80a32e80cbed65eba2103201a7c94710": "Model nebyl nalezen: {0}",
|
||||
"830cb6c862f8f364e9064cea0026f701": "Načtení vztahu hasOne {0}.",
|
||||
"83cbdc2560ba9f09155ccfc63e08f1a1": "Vlastnost `{0}` nelze překonfigurovat pro `{1}`",
|
||||
"855eb8db89b4921c42072832d33d2dc2": "Neplatné heslo.",
|
||||
"855ecd4a64885ba272d782435f72a4d4": "Neznámé ID \"{0}\" \"{1}\".",
|
||||
"860d1a0b8bd340411fb32baa72867989": "Přenos nepodporuje přesměrování HTTP.",
|
||||
"86254879d01a60826a851066987703f2": "Přidejte související položku podle ID pro {0}.",
|
||||
"895b1f941d026870b3cc8e6af087c197": "{{username}} nebo {{email}} je povinné",
|
||||
"8ae418c605b6a45f2651be9b1677c180": "Neplatná vzdálená metoda: `{0}`",
|
||||
"8bab6720ecc58ec6412358c858a53484": "Hromadná aktualizace se nezdařila, konektor upravil neočekávaný počet záznamů: {0}",
|
||||
"8ecab7f534de38360bd1b1c88e880123": "Podřízené modely `{0}` nebudou dědit nově definované vzdálené metody {1}.",
|
||||
"93ba9a1d03da3b7696332d3f155c5bb7": "\t HTML:{0}",
|
||||
"97795efe0c3eb7f35ce8cf8cfe70682b": "Konfiguraci `{0}` chybí vlastnost {{`dataSource`}}.\nPoužijte `null` nebo `false` k označení modelů, které nejsou připojeny k žádnému zdroji dat.",
|
||||
"9e3cbc1d5a9347cdcf6d1b4a6dbb55b7": "Načte vztah belongsTo {0}.",
|
||||
"a50d10fc6e0959b220e085454c40381e": "Uživatel nebyl nalezen: {0}",
|
||||
"b6f740aeb6f2eb9bee9cb049dbfe6a28": "Neznámý \"{0}\" {{key}} \"{1}\".",
|
||||
"ba96498b10c179f9cd75f75c8def4f70": "{{realm}} je povinný",
|
||||
"c0057a569ff9d3b509bac61a4b2f605d": "Odstraní všechny {0} tohoto modelu.",
|
||||
"c2b5d51f007178170ca3952d59640ca4": "Nelze odstranit {0} změn: \n {1}",
|
||||
"c4ee6d177c974532c3552d2f98eb72ea": "Middleware {0} byl odebrán ve verzi 3.0. Další podrobnosti naleznete v části {1}.",
|
||||
"c61a5a02ba3801a892308f70f5d55a14": "Metadata vzdálené komunikace {{\"isStatic\"}} jsou zamítnuta. Místo toho zadejte {{\"prototype.name\"}} v názvu metody pro {{isStatic=false}}.",
|
||||
"c68a93f0a9524fed4ff64372fc90c55f": "Je třeba zadat platný e-mail",
|
||||
"cd0412f2f33a4a2a316acc834f3f21a6": "Je třeba zadat {{id}} nebo {{data}}",
|
||||
"d5552322de5605c58b62f47ad26d2716": "{{`app.boot`}} byl odebrán, místo toho použijte nový modul {{loopback-boot}}",
|
||||
"d6f43b266533b04d442bdb3955622592": "Vytvoří novou instanci v {0} tohoto modelu.",
|
||||
"da13d3cdf21330557254670dddd8c5c7": "Vypočte {0} z {1}.",
|
||||
"dc568bee32deb0f6eaf63e73b20e8ceb": "Ignorování neobjektového nastavení \"methods\" \"{0}\".",
|
||||
"e4434de4bb8f5a3cd1d416e4d80d7e0b": "Neznámé \"{0}\" {{id}} \"{1}\".",
|
||||
"e92aa25b6b864e3454b65a7c422bd114": "Hromadná aktualizace se nezdařila, konektor odstranil neočekávaný počet záznamů: {0}",
|
||||
"ea63d226b6968e328bdf6876010786b5": "Nelze použít hromadné aktualizace, konektor správně nehlásí počet odstraněných záznamů.",
|
||||
"ead044e2b4bce74b4357f8a03fb78ec4": "Nelze volat {0}.{1}(). Metoda {2} nebyla nastavena. {{KeyValueModel}} nebyl správně připojen ke {{DataSource}}!",
|
||||
"ecb06666ef95e5db27a5ac1d6a17923b": "\t K:{0}",
|
||||
"f0aed00a3d3d0b97d6594e4b70e0c201": "\t PŘENOS:{0}",
|
||||
"f0bd73df8714cefb925e3b8da2f4c5f6": "výsledek: {0}",
|
||||
"f1d4ac54357cc0932f385d56814ba7e4": "Konflikt",
|
||||
"f66ae3cf379b2fce28575a3282defe1a": "Odstraní {0} tohoto modelu.",
|
||||
"f8e26bcca62a47f579562f1cd2c785ff": "Přepracujte aplikaci tak, aby používala oficiální řešení pro vložení argumentu \"options\" z kontextu požadavku,\nviz {0}"
|
||||
}
|
||||
|
|
@ -3,11 +3,13 @@
|
|||
"04bd8af876f001ceaf443aad6a9002f9": "Für die Authentifizierung muss Modell {0} definiert sein.",
|
||||
"095afbf2f1f0e5be678f5dac5c54e717": "Zugriff verweigert",
|
||||
"0caffe1d763c8cca6a61814abe33b776": "E-Mail ist erforderlich",
|
||||
"0da38687fed24275c1547e815914a8e3": "Zugehöriges Element nach ID für {0} löschen.",
|
||||
"0e21aad369dd09e1965c11949303cefd": "Die Remote-Anbindungs-Metadaten {{\"isStatic\"}} für {0}.{1} entsprechen nicht dem namensbasierten Stil der neuen Methode.",
|
||||
"10e01c895dc0b2fecc385f9f462f1ca6": "eine Liste mit Farben ist verfügbar unter {{http://localhost:3000/colors}}",
|
||||
"1b2a6076dccbe91a56f1672eb3b8598c": "Der Antworthauptteil enthält Eigenschaften des bei der Anmeldung erstellten {{AccessToken}}.\nAbhängig vom Wert des Parameters 'include' kann der Hauptteil zusätzliche Eigenschaften enthalten:\n\n - user - U+007BUserU+007D - Daten des derzeit angemeldeten Benutzers. {{(`include=user`)}}\n\n",
|
||||
"1d7833c3ca2f05fdad8fad7537531c40": "\t BETREFF:{0}",
|
||||
"1e85f822b547a75d7d385048030e4ecb": "Erstellt: {0}",
|
||||
"22fe62fa8d595b72c62208beddaa2a56": "Zugehöriges Element nach ID für {0} aktualisieren.",
|
||||
"275f22ab95671f095640ca99194b7635": "\t VON:{0}",
|
||||
"2860bccdf9ef1e350c1a38932ed12173": "{0} wurde in Version 3.0 entfernt. Siehe {1} für weitere Details.",
|
||||
"2d3071e3b18681c80a090dc0efbdb349": "{0} mit ID {1} konnte nicht gefunden werden",
|
||||
|
@ -16,6 +18,7 @@
|
|||
"3438fab56cc7ab92dfd88f0497e523e0": "Die relations-Eigenschaft der Konfiguration '{0}' muss ein Objekt sein",
|
||||
"3591f1d3e115b46f9f195df5ca548a6a": "Modell nicht gefunden: Modell '{0}' bietet das unbekannte Modell '{1}' an.",
|
||||
"35e5252c62d80f8c54a5290d30f4c7d0": "Bestätigen Sie Ihre E-Mail-Adresse, indem Sie diesen Link in einem Web-Browser öffnen:\n\t{0}",
|
||||
"37bcd4b50dfae98734772e39ffb1ea3d": "Das eingegebene Kennwort war zu lang. Maximale Länge: {0} (eingegeben: {1})",
|
||||
"3aae63bb7e8e046641767571c1591441": "Anmeldung fehlgeschlagen, da die E-Mail-Adresse nicht bestätigt wurde",
|
||||
"3aecb24fa8bdd3f79d168761ca8a6729": "Unbekannte {{middleware}}-Phase {0}",
|
||||
"3ca45aa6f705c46a4c598a900716f086": "{0} verwendet Modelleinstellung {1}, die nicht mehr verfügbar ist.",
|
||||
|
@ -27,51 +30,64 @@
|
|||
"44a6c8b1ded4ed653d19ddeaaf89a606": "E-Mail nicht gefunden",
|
||||
"4a4f04a4e480fc5d4ee73b84d9a4b904": "E-Mail senden:",
|
||||
"4b494de07f524703ac0879addbd64b13": "E-Mail-Adresse wurde nicht bestätigt",
|
||||
"528325f3cbf1b0ab9a08447515daac9a": "{0} von diesem Modell aktualisieren.",
|
||||
"543d19bad5e47ee1e9eb8af688e857b4": "Fremdschlüssel für {0}.",
|
||||
"57b87ae0e65f6ab7a2e3e6cbdfca49a4": "Kann Datenquelle {0} nicht erstellen: {1}",
|
||||
"5858e63efaa0e4ad86b61c0459ea32fa": "Sie müssen das {{Email}}-Modell mit einem {{Mail}}-Konnektor verbinden",
|
||||
"598ff0255ffd1d1b71e8de55dbe2c034": "Vorhandensein von {0}-Beziehung zu einem Element nach ID prüfen.",
|
||||
"5a36cc6ba0cc27c754f6c5ed6015ea3c": "{0}-Beziehung zu einem Element nach ID entfernen.",
|
||||
"5e81ad3847a290dc650b47618b9cbc7e": "Anmeldung fehlgeschlagen",
|
||||
"5fa3afb425819ebde958043e598cb664": "Modell mit {{id}} {0} konnte nicht gefunden werden",
|
||||
"61e5deebaf44d68f4e6a508f30cc31a3": "Beziehung '{0} ist für Modell {1} nicht vorhanden",
|
||||
"62e8b0a733417978bab22c8dacf5d7e6": "Massenaktualisierungen können nicht angewendet werden, der Konnektor meldet die Anzahl aktualisierter Datensätze nicht richtig.",
|
||||
"63a091ced88001ab6acb58f61ec041c5": "\t TEXT:{0}",
|
||||
"651f0b3cbba001635152ec3d3d954d0a": "Zugehöriges Element nach ID für {0} suchen.",
|
||||
"6bc376432cd9972cf991aad3de371e78": "Fehlende Daten für Änderung: {0}",
|
||||
"705c2d456a3e204c4af56e671ec3225c": "{{accessToken}} konnte nicht gefunden werden",
|
||||
"734a7bebb65e10899935126ba63dd51f": "Die options-Eigenschaft der Konfiguration '{0}' muss ein Objekt sein",
|
||||
"779467f467862836e19f494a37d6ab77": "Die acls-Eigenschaft der Konfiguration '{0}' muss eine Reihe von Objekten sein",
|
||||
"7bc7b301ad9c4fc873029d57fb9740fe": "Abfrage von {0} von {1}.",
|
||||
"7c837b88fd0e509bd3fc722d7ddf0711": "Fremdschlüssel für {0}",
|
||||
"7d5e7ed0efaedf3f55f380caae0df8b8": "Meine erste mobile Anwendung",
|
||||
"7e0fca41d098607e1c9aa353c67e0fa1": "Ungültiges Zugriffstoken",
|
||||
"7e287fc885d9fdcf42da3a12f38572c1": "Berechtigung erforderlich",
|
||||
"7ea04ea91aac3cb7ce0ddd96b7ff1fa4": "{{accessToken}} muss sich abmelden",
|
||||
"80a32e80cbed65eba2103201a7c94710": "Modell nicht gefunden: {0}",
|
||||
"830cb6c862f8f364e9064cea0026f701": "Ruft hasOne-Beziehung {0} ab.",
|
||||
"83cbdc2560ba9f09155ccfc63e08f1a1": "Eigenschaft '{0}' kann für {1} nicht rekonfiguriert werden",
|
||||
"855eb8db89b4921c42072832d33d2dc2": "Ungültiges Kennwort.",
|
||||
"855ecd4a64885ba272d782435f72a4d4": "\"{0}\" unbekannt, ID \"{1}\".",
|
||||
"860d1a0b8bd340411fb32baa72867989": "Die Transportmethode unterstützt keine HTTP-Umleitungen.",
|
||||
"86254879d01a60826a851066987703f2": "Zugehöriges Element nach ID für {0} hinzufügen.",
|
||||
"895b1f941d026870b3cc8e6af087c197": "{{username}} oder {{email}} ist erforderlich",
|
||||
"8a17c5ef611e2e7535792316e66b8fca": "Kennwort zu lang: {0}",
|
||||
"8ae418c605b6a45f2651be9b1677c180": "Ungültige Remote-Methode: '{0}'",
|
||||
"8bab6720ecc58ec6412358c858a53484": "Massenaktualisierung fehlgeschlagen, der Konnektor hat eine unerwartete Anzahl an Datensätzen geändert: {0}",
|
||||
"8ecab7f534de38360bd1b1c88e880123": "Untergeordnete Modelle von `{0}` übernehmen nicht die neu definierten Remote-Methoden {1}.",
|
||||
"93ba9a1d03da3b7696332d3f155c5bb7": "\t HTML:{0}",
|
||||
"97795efe0c3eb7f35ce8cf8cfe70682b": "Der Konfiguration von {0} fehlt die {{`dataSource`}}-Eigenschaft.\nVerwenden Sie 'null' oder 'false', um Modelle zu kennzeichnen, die mit keiner Datenquelle verbunden sind.",
|
||||
"9e3cbc1d5a9347cdcf6d1b4a6dbb55b7": "Ruft belongsTo-Beziehung {0} ab.",
|
||||
"a50d10fc6e0959b220e085454c40381e": "Benutzer nicht gefunden: {0}",
|
||||
"b6f740aeb6f2eb9bee9cb049dbfe6a28": "\"{0}\" unbekannt, {{key}} \"{1}\".",
|
||||
"ba96498b10c179f9cd75f75c8def4f70": "{{realm}} ist erforderlich",
|
||||
"c0057a569ff9d3b509bac61a4b2f605d": "Löscht alle {0} von diesem Modell.",
|
||||
"c2b5d51f007178170ca3952d59640ca4": "{0} Änderungen können nicht behoben werden:\n{1}",
|
||||
"c4ee6d177c974532c3552d2f98eb72ea": "Middleware {0} wurde in Version 3.0 entfernt. Siehe {1} für weitere Details.",
|
||||
"c61a5a02ba3801a892308f70f5d55a14": "Metadaten {{\"isStatic\"}} für Remote-Anbindung sind veraltet. Bitte geben Sie {{\"prototype.name\"}} anstelle von {{isStatic=false}} beim Methodennamen an.",
|
||||
"c68a93f0a9524fed4ff64372fc90c55f": "Eine gültige E-Mail-Adresse muss angegeben werden",
|
||||
"cd0412f2f33a4a2a316acc834f3f21a6": "muss {{id}} oder {{data}} angeben",
|
||||
"d5552322de5605c58b62f47ad26d2716": "{{`app.boot`}} wurde entfernt, verwenden Sie stattdessen das neue Modul {{loopback-boot}}",
|
||||
"d6f43b266533b04d442bdb3955622592": "Erstellt eine neue Instanz in {0} von diesem Modell.",
|
||||
"da13d3cdf21330557254670dddd8c5c7": "Zählt {0} von {1}.",
|
||||
"dc568bee32deb0f6eaf63e73b20e8ceb": "Nicht-Objekt-Einstellung \"{0}\" von \"methods\" wird ignoriert.",
|
||||
"e4434de4bb8f5a3cd1d416e4d80d7e0b": "Unbekannte \"{0}\" {{id}} \"{1}\".",
|
||||
"e92aa25b6b864e3454b65a7c422bd114": "Massenaktualisierung fehlgeschlagen, der Konnektor hat eine unerwartete Anzahl an Datensätzen gelöscht : {0}",
|
||||
"e4434de4bb8f5a3cd1d416e4d80d7e0b": "\"{0}\" unbekannt, {{id}} \"{1}\".",
|
||||
"e92aa25b6b864e3454b65a7c422bd114": "Massenaktualisierung fehlgeschlagen, der Konnektor hat eine unerwartete Anzahl an Datensätzen gelöscht: {0}",
|
||||
"ea63d226b6968e328bdf6876010786b5": "Massenaktualisierungen können nicht angewendet werden, der Konnektor meldet die Anzahl gelöschter Datensätze nicht richtig.",
|
||||
"ead044e2b4bce74b4357f8a03fb78ec4": "{0}.{1}() kann nicht aufgerufen werden. Die Methode {2} wurde nicht konfiguriert. {{KeyValueModel}} wurde nicht ordnungsgemäß an eine {{DataSource}} angehängt!",
|
||||
"ecb06666ef95e5db27a5ac1d6a17923b": "\t AN:{0}",
|
||||
"f0aed00a3d3d0b97d6594e4b70e0c201": "\t TRANSPORTMETHODE:{0}",
|
||||
"f0bd73df8714cefb925e3b8da2f4c5f6": "Ergebnis:{0}",
|
||||
"f1d4ac54357cc0932f385d56814ba7e4": "Konflikt",
|
||||
"f58cdc481540cd1f69a4aa4da2e37981": "Ungültiges Kennwort: {0}",
|
||||
"f66ae3cf379b2fce28575a3282defe1a": "Löscht {0} von diesem Modell.",
|
||||
"f8e26bcca62a47f579562f1cd2c785ff": "Überarbeiten Sie Ihre App, damit sie die offizielle Lösung für eine Injection des Arguments \"options\" aus dem Anforderungskontext verwendet,\nsiehe {0}"
|
||||
}
|
||||
|
||||
|
|
|
@ -3,11 +3,13 @@
|
|||
"04bd8af876f001ceaf443aad6a9002f9": "Authentication requires model {0} to be defined.",
|
||||
"095afbf2f1f0e5be678f5dac5c54e717": "Access Denied",
|
||||
"0caffe1d763c8cca6a61814abe33b776": "Email is required",
|
||||
"0da38687fed24275c1547e815914a8e3": "Delete a related item by id for {0}.",
|
||||
"0e21aad369dd09e1965c11949303cefd": "Remoting metadata for {0}.{1} {{\"isStatic\"}} does not match new method name-based style.",
|
||||
"10e01c895dc0b2fecc385f9f462f1ca6": "a list of colors is available at {{http://localhost:3000/colors}}",
|
||||
"1b2a6076dccbe91a56f1672eb3b8598c": "The response body contains properties of the {{AccessToken}} created on login.\nDepending on the value of `include` parameter, the body may contain additional properties:\n\n - `user` - `U+007BUserU+007D` - Data of the currently logged in user. {{(`include=user`)}}\n\n",
|
||||
"1d7833c3ca2f05fdad8fad7537531c40": "\t SUBJECT:{0}",
|
||||
"1e85f822b547a75d7d385048030e4ecb": "Created: {0}",
|
||||
"22fe62fa8d595b72c62208beddaa2a56": "Update a related item by id for {0}.",
|
||||
"275f22ab95671f095640ca99194b7635": "\t FROM:{0}",
|
||||
"2860bccdf9ef1e350c1a38932ed12173": "{0} was removed in version 3.0. See {1} for more details.",
|
||||
"2d3071e3b18681c80a090dc0efbdb349": "could not find {0} with id {1}",
|
||||
|
@ -16,6 +18,7 @@
|
|||
"3438fab56cc7ab92dfd88f0497e523e0": "The relations property of `{0}` configuration must be an object",
|
||||
"3591f1d3e115b46f9f195df5ca548a6a": "Model not found: model `{0}` is extending an unknown model `{1}`.",
|
||||
"35e5252c62d80f8c54a5290d30f4c7d0": "Please verify your email by opening this link in a web browser:\n\t{0}",
|
||||
"37bcd4b50dfae98734772e39ffb1ea3d": "The password entered was too long. Max length is {0} (entered {1})",
|
||||
"3aae63bb7e8e046641767571c1591441": "login failed as the email has not been verified",
|
||||
"3aecb24fa8bdd3f79d168761ca8a6729": "Unknown {{middleware}} phase {0}",
|
||||
"3ca45aa6f705c46a4c598a900716f086": "{0} is using model setting {1} which is no longer available.",
|
||||
|
@ -27,41 +30,54 @@
|
|||
"44a6c8b1ded4ed653d19ddeaaf89a606": "Email not found",
|
||||
"4a4f04a4e480fc5d4ee73b84d9a4b904": "Sending Mail:",
|
||||
"4b494de07f524703ac0879addbd64b13": "Email has not been verified",
|
||||
"528325f3cbf1b0ab9a08447515daac9a": "Update {0} of this model.",
|
||||
"543d19bad5e47ee1e9eb8af688e857b4": "Foreign key for {0}.",
|
||||
"57b87ae0e65f6ab7a2e3e6cbdfca49a4": "Cannot create data source {0}: {1}",
|
||||
"5858e63efaa0e4ad86b61c0459ea32fa": "You must connect the {{Email}} Model to a {{Mail}} connector",
|
||||
"598ff0255ffd1d1b71e8de55dbe2c034": "Check the existence of {0} relation to an item by id.",
|
||||
"5a36cc6ba0cc27c754f6c5ed6015ea3c": "Remove the {0} relation to an item by id.",
|
||||
"5e81ad3847a290dc650b47618b9cbc7e": "login failed",
|
||||
"5fa3afb425819ebde958043e598cb664": "could not find a model with {{id}} {0}",
|
||||
"61e5deebaf44d68f4e6a508f30cc31a3": "Relation `{0}` does not exist for model `{1}`",
|
||||
"62e8b0a733417978bab22c8dacf5d7e6": "Cannot apply bulk updates, the connector does not correctly report the number of updated records.",
|
||||
"63a091ced88001ab6acb58f61ec041c5": "\t TEXT:{0}",
|
||||
"651f0b3cbba001635152ec3d3d954d0a": "Find a related item by id for {0}.",
|
||||
"6bc376432cd9972cf991aad3de371e78": "Missing data for change: {0}",
|
||||
"705c2d456a3e204c4af56e671ec3225c": "Could not find {{accessToken}}",
|
||||
"734a7bebb65e10899935126ba63dd51f": "The options property of `{0}` configuration must be an object",
|
||||
"779467f467862836e19f494a37d6ab77": "The acls property of `{0}` configuration must be an array of objects",
|
||||
"7bc7b301ad9c4fc873029d57fb9740fe": "Queries {0} of {1}.",
|
||||
"7c837b88fd0e509bd3fc722d7ddf0711": "Foreign key for {0}",
|
||||
"7d5e7ed0efaedf3f55f380caae0df8b8": "My first mobile application",
|
||||
"7e0fca41d098607e1c9aa353c67e0fa1": "Invalid Access Token",
|
||||
"7e287fc885d9fdcf42da3a12f38572c1": "Authorization Required",
|
||||
"7ea04ea91aac3cb7ce0ddd96b7ff1fa4": "{{accessToken}} is required to logout",
|
||||
"80a32e80cbed65eba2103201a7c94710": "Model not found: {0}",
|
||||
"830cb6c862f8f364e9064cea0026f701": "Fetches hasOne relation {0}.",
|
||||
"83cbdc2560ba9f09155ccfc63e08f1a1": "Property `{0}` cannot be reconfigured for `{1}`",
|
||||
"855eb8db89b4921c42072832d33d2dc2": "Invalid password.",
|
||||
"855ecd4a64885ba272d782435f72a4d4": "Unknown \"{0}\" id \"{1}\".",
|
||||
"860d1a0b8bd340411fb32baa72867989": "The transport does not support HTTP redirects.",
|
||||
"86254879d01a60826a851066987703f2": "Add a related item by id for {0}.",
|
||||
"895b1f941d026870b3cc8e6af087c197": "{{username}} or {{email}} is required",
|
||||
"8a17c5ef611e2e7535792316e66b8fca": "Password too long: {0}",
|
||||
"8ae418c605b6a45f2651be9b1677c180": "Invalid remote method: `{0}`",
|
||||
"8bab6720ecc58ec6412358c858a53484": "Bulk update failed, the connector has modified unexpected number of records: {0}",
|
||||
"8ecab7f534de38360bd1b1c88e880123": "Child models of `{0}` will not inherit newly defined remote methods {1}.",
|
||||
"93ba9a1d03da3b7696332d3f155c5bb7": "\t HTML:{0}",
|
||||
"97795efe0c3eb7f35ce8cf8cfe70682b": "The configuration of `{0}` is missing {{`dataSource`}} property.\nUse `null` or `false` to mark models not attached to any data source.",
|
||||
"9e3cbc1d5a9347cdcf6d1b4a6dbb55b7": "Fetches belongsTo relation {0}.",
|
||||
"a50d10fc6e0959b220e085454c40381e": "User not found: {0}",
|
||||
"b6f740aeb6f2eb9bee9cb049dbfe6a28": "Unknown \"{0}\" {{key}} \"{1}\".",
|
||||
"ba96498b10c179f9cd75f75c8def4f70": "{{realm}} is required",
|
||||
"c0057a569ff9d3b509bac61a4b2f605d": "Deletes all {0} of this model.",
|
||||
"c2b5d51f007178170ca3952d59640ca4": "Cannot rectify {0} changes:\n{1}",
|
||||
"c4ee6d177c974532c3552d2f98eb72ea": "{0} middleware was removed in version 3.0. See {1} for more details.",
|
||||
"c61a5a02ba3801a892308f70f5d55a14": "Remoting metadata {{\"isStatic\"}} is deprecated. Please specify {{\"prototype.name\"}} in method name instead for {{isStatic=false}}.",
|
||||
"c68a93f0a9524fed4ff64372fc90c55f": "Must provide a valid email",
|
||||
"cd0412f2f33a4a2a316acc834f3f21a6": "must specify an {{id}} or {{data}}",
|
||||
"d5552322de5605c58b62f47ad26d2716": "{{`app.boot`}} was removed, use the new module {{loopback-boot}} instead",
|
||||
"d6f43b266533b04d442bdb3955622592": "Creates a new instance in {0} of this model.",
|
||||
"da13d3cdf21330557254670dddd8c5c7": "Counts {0} of {1}.",
|
||||
"dc568bee32deb0f6eaf63e73b20e8ceb": "Ignoring non-object \"methods\" setting of \"{0}\".",
|
||||
"e4434de4bb8f5a3cd1d416e4d80d7e0b": "Unknown \"{0}\" {{id}} \"{1}\".",
|
||||
"e92aa25b6b864e3454b65a7c422bd114": "Bulk update failed, the connector has deleted unexpected number of records: {0}",
|
||||
|
@ -71,6 +87,6 @@
|
|||
"f0aed00a3d3d0b97d6594e4b70e0c201": "\t TRANSPORT:{0}",
|
||||
"f0bd73df8714cefb925e3b8da2f4c5f6": "result:{0}",
|
||||
"f1d4ac54357cc0932f385d56814ba7e4": "Conflict",
|
||||
"f58cdc481540cd1f69a4aa4da2e37981": "Invalid password: {0}",
|
||||
"f66ae3cf379b2fce28575a3282defe1a": "Deletes {0} of this model.",
|
||||
"f8e26bcca62a47f579562f1cd2c785ff": "Please rework your app to use the offical solution for injecting \"options\" argument from request context,\nsee {0}"
|
||||
}
|
||||
|
|
|
@ -3,11 +3,13 @@
|
|||
"04bd8af876f001ceaf443aad6a9002f9": "La autenticación requiere la definición del modelo {0}.",
|
||||
"095afbf2f1f0e5be678f5dac5c54e717": "Acceso denegado",
|
||||
"0caffe1d763c8cca6a61814abe33b776": "El correo electrónico es obligatorio",
|
||||
"0e21aad369dd09e1965c11949303cefd": "Metadatos de interacción remota para {0}.{1} {{\"isStatic\"}} no coincide con el estilo basado en nombre del nuevo método.",
|
||||
"0da38687fed24275c1547e815914a8e3": "Suprimir un elemento relacionado por id para {0}.",
|
||||
"0e21aad369dd09e1965c11949303cefd": "Los metadatos de interacción remota para {0}.{1} {{\"isStatic\"}} no coinciden con el estilo basado en nombre del nuevo método.",
|
||||
"10e01c895dc0b2fecc385f9f462f1ca6": "una lista de colores está disponible en {{http://localhost:3000/colors}}",
|
||||
"1b2a6076dccbe91a56f1672eb3b8598c": "El cuerpo de respuesta contiene propiedades de la {{AccessToken}} creada durante el inicio de la sesión.\nDependiendo del valor del parámetro `include`, el cuerpo puede contener propiedades adicionales:\n\n - `user` - `U+007BUserU+007D` - Datos del usuario conectado actualmente. {{(`include=user`)}}\n\n",
|
||||
"1d7833c3ca2f05fdad8fad7537531c40": "\t ASUNTO:{0}",
|
||||
"1e85f822b547a75d7d385048030e4ecb": "Creado: {0}",
|
||||
"22fe62fa8d595b72c62208beddaa2a56": "Actualizar un elemento relacionado por id para {0}.",
|
||||
"275f22ab95671f095640ca99194b7635": "\t DESDE:{0}",
|
||||
"2860bccdf9ef1e350c1a38932ed12173": "{0} se ha eliminado en la versión 3.0. Consulte {1} para obtener detalles.",
|
||||
"2d3071e3b18681c80a090dc0efbdb349": "no se ha encontrado {0} con el ID {1}",
|
||||
|
@ -16,6 +18,7 @@
|
|||
"3438fab56cc7ab92dfd88f0497e523e0": "La configuración de la propiedad relations de `{0}` debe ser un objeto",
|
||||
"3591f1d3e115b46f9f195df5ca548a6a": "Modelo no encontrado: el modelo `{0}` está ampliando un modelo desconocido `{1}`.",
|
||||
"35e5252c62d80f8c54a5290d30f4c7d0": "Verifique su correo electrónico abriendo este enlace en un navegador:\n\t {0}",
|
||||
"37bcd4b50dfae98734772e39ffb1ea3d": "La contraseña especificada es demasiado larga. La longitud máxima es {0} (se ha especificado {1})",
|
||||
"3aae63bb7e8e046641767571c1591441": "el inicio de sesión ha fallado porque el correo electrónico no ha sido verificado",
|
||||
"3aecb24fa8bdd3f79d168761ca8a6729": "Fase de {{middleware}} desconocida {0}",
|
||||
"3ca45aa6f705c46a4c598a900716f086": "{0} utiliza el valor de modelo {1}, que ya no está disponible.",
|
||||
|
@ -26,42 +29,55 @@
|
|||
"42a36bac5cf03c4418d664500c81047a": "La opción de {{DataSource}} {{\"defaultForType\"}} ya no está soportada",
|
||||
"44a6c8b1ded4ed653d19ddeaaf89a606": "Correo electrónico no encontrado",
|
||||
"4a4f04a4e480fc5d4ee73b84d9a4b904": "Enviando correo:",
|
||||
"4b494de07f524703ac0879addbd64b13": "El correo electrónico no se ha verificado",
|
||||
"4b494de07f524703ac0879addbd64b13": "No se ha verificado el correo electrónico",
|
||||
"528325f3cbf1b0ab9a08447515daac9a": "Actualizar {0} de este modelo.",
|
||||
"543d19bad5e47ee1e9eb8af688e857b4": "Clave foránea para {0}.",
|
||||
"57b87ae0e65f6ab7a2e3e6cbdfca49a4": "No se puede crear el origen de datos {0}: {1}",
|
||||
"5858e63efaa0e4ad86b61c0459ea32fa": "Debe conectar el modelo de {{Email}} a un conector de {{Mail}}",
|
||||
"598ff0255ffd1d1b71e8de55dbe2c034": "Comprobar la existencia de la relación {0} con un elemento por id.",
|
||||
"5a36cc6ba0cc27c754f6c5ed6015ea3c": "Eliminar la relación {0} con un elemento por id.",
|
||||
"5e81ad3847a290dc650b47618b9cbc7e": "el inicio de sesión ha fallado",
|
||||
"5fa3afb425819ebde958043e598cb664": "no se ha encontrado un modelo con {{id}} {0}",
|
||||
"61e5deebaf44d68f4e6a508f30cc31a3": "La relación `{0}` no existe para el modelo `{1}`",
|
||||
"62e8b0a733417978bab22c8dacf5d7e6": "No pueden aplicarse actualizaciones masivas, el conector no notifica correctamente el número de registros actualizados.",
|
||||
"63a091ced88001ab6acb58f61ec041c5": "\t TEXTO:{0}",
|
||||
"651f0b3cbba001635152ec3d3d954d0a": "Buscar un elemento relacionado por id para {0}.",
|
||||
"6bc376432cd9972cf991aad3de371e78": "Faltan datos para el cambio: {0}",
|
||||
"705c2d456a3e204c4af56e671ec3225c": "No se ha encontrado {{accessToken}}",
|
||||
"734a7bebb65e10899935126ba63dd51f": "La configuración de la propiedad de options de `{0}` debe ser un objeto",
|
||||
"779467f467862836e19f494a37d6ab77": "La configuración de la propiedad acls de `{0}` debe ser una matriz de objetos",
|
||||
"7bc7b301ad9c4fc873029d57fb9740fe": "{0} consultas de {1}.",
|
||||
"7c837b88fd0e509bd3fc722d7ddf0711": "Clave foránea para {0}",
|
||||
"7d5e7ed0efaedf3f55f380caae0df8b8": "Mi primera aplicación móvil",
|
||||
"7e0fca41d098607e1c9aa353c67e0fa1": "Señal de acceso no válida",
|
||||
"7e287fc885d9fdcf42da3a12f38572c1": "Autorización necesaria",
|
||||
"7ea04ea91aac3cb7ce0ddd96b7ff1fa4": "Es necesario {{accessToken}} para cerrar la sesión",
|
||||
"80a32e80cbed65eba2103201a7c94710": "No se ha encontrado el modelo: {0}",
|
||||
"830cb6c862f8f364e9064cea0026f701": "Capta la relación hasOne {0}.",
|
||||
"83cbdc2560ba9f09155ccfc63e08f1a1": "La propiedad `{0}` no puede reconfigurarse para `{1}`",
|
||||
"855eb8db89b4921c42072832d33d2dc2": "Contraseña no válida.",
|
||||
"855ecd4a64885ba272d782435f72a4d4": "Id de \"{0}\" desconocido \"{1}\".",
|
||||
"860d1a0b8bd340411fb32baa72867989": "El transporte no admite redirecciones HTTP.",
|
||||
"86254879d01a60826a851066987703f2": "Añadir un elemento relacionado por id para {0}.",
|
||||
"895b1f941d026870b3cc8e6af087c197": "{{username}} o {{email}} es obligatorio",
|
||||
"8a17c5ef611e2e7535792316e66b8fca": "Contraseña demasiado larga: {0}",
|
||||
"8ae418c605b6a45f2651be9b1677c180": "Método remoto no válido: `{0}`",
|
||||
"8bab6720ecc58ec6412358c858a53484": "La actualización masiva ha fallado, el conector ha modificado un número de registros inesperado: {0}",
|
||||
"8ecab7f534de38360bd1b1c88e880123": "Los modelos hijo de `{0}` no heredarán los métodos remotos definidos recientemente {1}.",
|
||||
"93ba9a1d03da3b7696332d3f155c5bb7": "\t HTML:{0}",
|
||||
"97795efe0c3eb7f35ce8cf8cfe70682b": "En la configuración de `{0}` falta la propiedad {{`dataSource`}}.\nUtilice `null` o `false` para marcar los modelos no conectados a ningún origen de datos.",
|
||||
"9e3cbc1d5a9347cdcf6d1b4a6dbb55b7": "Capta la relación belongsTo {0}.",
|
||||
"a50d10fc6e0959b220e085454c40381e": "No se ha encontrado el usuario: {0}",
|
||||
"b6f740aeb6f2eb9bee9cb049dbfe6a28": "{{key}} de \"{0}\" desconocido \"{1}\".",
|
||||
"ba96498b10c179f9cd75f75c8def4f70": "{{realm}} es obligatorio",
|
||||
"c0057a569ff9d3b509bac61a4b2f605d": "Suprime todos los {0} de este modelo.",
|
||||
"c2b5d51f007178170ca3952d59640ca4": "No se pueden rectificar los cambios de {0}:\n{1}",
|
||||
"c4ee6d177c974532c3552d2f98eb72ea": "El middleware {0} se ha eliminado en la versión 3.0. Consulte {1} para obtener detalles.",
|
||||
"c61a5a02ba3801a892308f70f5d55a14": "Los metadatos de interacción remota {{\"isStatic\"}} están en desuso. Especifique {{\"prototype.name\"}} en el nombre de método en lugar de {{isStatic=false}}.",
|
||||
"c68a93f0a9524fed4ff64372fc90c55f": "Debe proporcionar un correo electrónico válido",
|
||||
"cd0412f2f33a4a2a316acc834f3f21a6": "debe especificar un {{id}} o {{data}}",
|
||||
"d5552322de5605c58b62f47ad26d2716": "{{`app.boot`}} se ha eliminado, utilice el nuevo módulo {{loopback-boot}} en su lugar",
|
||||
"d6f43b266533b04d442bdb3955622592": "Crea una nueva instancia en {0} de este modelo.",
|
||||
"da13d3cdf21330557254670dddd8c5c7": "Recuentos {0} de {1}.",
|
||||
"dc568bee32deb0f6eaf63e73b20e8ceb": "Se ignora el valor \"methods\" no de objeto de \"{0}\".",
|
||||
"e4434de4bb8f5a3cd1d416e4d80d7e0b": "{{id}} de \"{0}\" desconocido \"{1}\".",
|
||||
"e92aa25b6b864e3454b65a7c422bd114": "La actualización masiva ha fallado, el conector ha suprimido un número de registros inesperado: {0}",
|
||||
|
@ -71,7 +87,7 @@
|
|||
"f0aed00a3d3d0b97d6594e4b70e0c201": "\t TRANSPORTE:{0}",
|
||||
"f0bd73df8714cefb925e3b8da2f4c5f6": "resultado:{0}",
|
||||
"f1d4ac54357cc0932f385d56814ba7e4": "Conflicto",
|
||||
"f58cdc481540cd1f69a4aa4da2e37981": "Contraseña no válida: {0}",
|
||||
"f66ae3cf379b2fce28575a3282defe1a": "Suprime {0} de este modelo.",
|
||||
"f8e26bcca62a47f579562f1cd2c785ff": "Reconfigure su aplicación para que utilice la solución oficial para inyectar el argumento \"options\" del contexto de solicitud, \nconsulte {0}"
|
||||
}
|
||||
|
||||
|
|
|
@ -3,11 +3,13 @@
|
|||
"04bd8af876f001ceaf443aad6a9002f9": "L'authentification exige que le modèle {0} soit défini.",
|
||||
"095afbf2f1f0e5be678f5dac5c54e717": "Accès refusé",
|
||||
"0caffe1d763c8cca6a61814abe33b776": "L'adresse électronique est obligatoire",
|
||||
"0e21aad369dd09e1965c11949303cefd": "Métadonnées remoting pour {0}.{1} {{\"isStatic\"}} ne correspond pas le style basé sur le nom de la nouvelle méthode.",
|
||||
"0da38687fed24275c1547e815914a8e3": "Supprimez un élément lié par id pour {0}.",
|
||||
"0e21aad369dd09e1965c11949303cefd": "Les métadonnées remoting pour {0}.{1} {{\"isStatic\"}} ne correspondent pas au style basé sur le nom de la nouvelle méthode.",
|
||||
"10e01c895dc0b2fecc385f9f462f1ca6": "une liste de couleurs est disponible sur {{http://localhost:3000/colors}}",
|
||||
"1b2a6076dccbe91a56f1672eb3b8598c": "Le corps de réponse contient les propriétés de {{AccessToken}} créées lors de la connexion.\nEn fonction de la valeur du paramètre `include`, le corps peut contenir des propriétés supplémentaires :\n\n - `user` - `U+007BUserU+007D` - Données de l'utilisateur connecté. {{(`include=user`)}}\n\n",
|
||||
"1d7833c3ca2f05fdad8fad7537531c40": "\t SUJET :{0}",
|
||||
"1d7833c3ca2f05fdad8fad7537531c40": "\t OBJET :{0}",
|
||||
"1e85f822b547a75d7d385048030e4ecb": "Création de : {0}",
|
||||
"22fe62fa8d595b72c62208beddaa2a56": "Mettez à jour un élément lié par id pour {0}.",
|
||||
"275f22ab95671f095640ca99194b7635": "\t DE :{0}",
|
||||
"2860bccdf9ef1e350c1a38932ed12173": "{0} a été supprimé dans la version 3.0. Pour plus de détails, voir {1}.",
|
||||
"2d3071e3b18681c80a090dc0efbdb349": "impossible de trouver {0} avec l'id {1}",
|
||||
|
@ -16,9 +18,10 @@
|
|||
"3438fab56cc7ab92dfd88f0497e523e0": "La propriété relations de la configuration `{0}` doit être un objet",
|
||||
"3591f1d3e115b46f9f195df5ca548a6a": "Modèle introuvable : le modèle `{0}` étend un modèle inconnu `{1}`.",
|
||||
"35e5252c62d80f8c54a5290d30f4c7d0": "Vérifiez votre courrier électronique en ouvrant ce lien dans un navigateur Web :\n\t{0}",
|
||||
"37bcd4b50dfae98734772e39ffb1ea3d": "Le mot de passe saisi était trop long. La longueur maximale est {0} ({1} caractères saisis)",
|
||||
"3aae63bb7e8e046641767571c1591441": "la connexion a échoué car l'adresse électronique n'a pas été vérifiée",
|
||||
"3aecb24fa8bdd3f79d168761ca8a6729": "Phase {{middleware}} inconnue {0}",
|
||||
"3ca45aa6f705c46a4c598a900716f086": "{0} utilise le paramètre de modèle {1} qui n'est plus disponible. ",
|
||||
"3ca45aa6f705c46a4c598a900716f086": "{0} utilise le paramètre de modèle {1} qui n'est plus disponible.",
|
||||
"3caaa84fc103d6d5612173ae6d43b245": "Jeton non valide : {0}",
|
||||
"3d617953470be16d0c2b32f0bcfbb5ee": "Merci pour votre inscription",
|
||||
"3d63008ccfb2af1db2142e8cc2716ace": "Avertissement : Aucun transport de courrier électronique n'est spécifié pour l'envoi d'un message électronique. Configurez un transport pour envoyer des messages électroniques.",
|
||||
|
@ -27,41 +30,54 @@
|
|||
"44a6c8b1ded4ed653d19ddeaaf89a606": "Adresse électronique introuvable",
|
||||
"4a4f04a4e480fc5d4ee73b84d9a4b904": "Envoi d'un message électronique :",
|
||||
"4b494de07f524703ac0879addbd64b13": "Le courrier électronique n'a pas été vérifié",
|
||||
"528325f3cbf1b0ab9a08447515daac9a": "Mettez à jour {0} de ce modèle.",
|
||||
"543d19bad5e47ee1e9eb8af688e857b4": "Clé externe pour {0}.",
|
||||
"57b87ae0e65f6ab7a2e3e6cbdfca49a4": "Impossible de créer la source de données {0} : {1}",
|
||||
"5858e63efaa0e4ad86b61c0459ea32fa": "Vous devez connecter le modèle {{Email}} à un connecteur {{Mail}}",
|
||||
"598ff0255ffd1d1b71e8de55dbe2c034": "Vérifiez l'existence de la relation {0} à un élément par id.",
|
||||
"5a36cc6ba0cc27c754f6c5ed6015ea3c": "Supprimez la relation {0} à un élément par id.",
|
||||
"5e81ad3847a290dc650b47618b9cbc7e": "échec de la connexion",
|
||||
"5fa3afb425819ebde958043e598cb664": "impossible de trouver un modèle avec {{id}} {0}",
|
||||
"61e5deebaf44d68f4e6a508f30cc31a3": "La relation `{0}` n'existe pas pour le modèle `{1}`",
|
||||
"62e8b0a733417978bab22c8dacf5d7e6": "Impossible d'appliquer des mises à jour en bloc ; le connecteur ne signale pas correctement le nombre d'enregistrements mis à jour.",
|
||||
"63a091ced88001ab6acb58f61ec041c5": "\t TEXTE :{0}",
|
||||
"651f0b3cbba001635152ec3d3d954d0a": "Recherchez un élément lié par id pour {0}.",
|
||||
"6bc376432cd9972cf991aad3de371e78": "Données manquantes pour le changement : {0}",
|
||||
"705c2d456a3e204c4af56e671ec3225c": "{{accessToken}} introuvable",
|
||||
"734a7bebb65e10899935126ba63dd51f": "La propriété options de la configuration `{0}` doit être un objet",
|
||||
"779467f467862836e19f494a37d6ab77": "La propriété acls de la configuration `{0}` doit être un tableau d'objets",
|
||||
"7bc7b301ad9c4fc873029d57fb9740fe": "Demandes {0} de {1}.",
|
||||
"7c837b88fd0e509bd3fc722d7ddf0711": "Clé externe pour {0}",
|
||||
"7d5e7ed0efaedf3f55f380caae0df8b8": "Ma première application mobile",
|
||||
"7e0fca41d098607e1c9aa353c67e0fa1": "Jeton d'accès non valide",
|
||||
"7e287fc885d9fdcf42da3a12f38572c1": "Autorisation requise",
|
||||
"7ea04ea91aac3cb7ce0ddd96b7ff1fa4": "{{accessToken}} est nécessaire pour la déconnexion",
|
||||
"80a32e80cbed65eba2103201a7c94710": "Modèle introuvable : {0}",
|
||||
"830cb6c862f8f364e9064cea0026f701": "Extrait la relation hasOne {0}.",
|
||||
"83cbdc2560ba9f09155ccfc63e08f1a1": "La propriété `{0}` ne peut pas être reconfigurée pour `{1}`",
|
||||
"855eb8db89b4921c42072832d33d2dc2": "Mot de passe non valide.",
|
||||
"855ecd4a64885ba272d782435f72a4d4": "ID \"{0}\" inconnu \"{1}\".",
|
||||
"860d1a0b8bd340411fb32baa72867989": "Le transport ne prend pas en charge les réacheminements HTTP.",
|
||||
"86254879d01a60826a851066987703f2": "Ajoutez un élément lié par id pour {0}.",
|
||||
"895b1f941d026870b3cc8e6af087c197": "{{username}} ou {{email}} est obligatoire",
|
||||
"8a17c5ef611e2e7535792316e66b8fca": "Mot de passe trop long : {0}",
|
||||
"8ae418c605b6a45f2651be9b1677c180": "Méthode distante non valide : `{0}`",
|
||||
"8bab6720ecc58ec6412358c858a53484": "La mise à jour en bloc a échoué ; le connecteur a modifié un nombre inattendu d'enregistrements : {0}",
|
||||
"8ecab7f534de38360bd1b1c88e880123": "Les modèles enfant de `{0}` n'hériteront pas des méthodes distantes nouvellement définies {1}.",
|
||||
"93ba9a1d03da3b7696332d3f155c5bb7": "\t HTML :{0}",
|
||||
"97795efe0c3eb7f35ce8cf8cfe70682b": "La propriété {{`dataSource`}} est manquante dans la configuration de `{0}`.\nUtilisez `null` ou `false` pour marquer les modèles non associés à une source de données.",
|
||||
"9e3cbc1d5a9347cdcf6d1b4a6dbb55b7": "Extrait la relation belongsTo {0}.",
|
||||
"a50d10fc6e0959b220e085454c40381e": "Utilisateur introuvable : {0}",
|
||||
"b6f740aeb6f2eb9bee9cb049dbfe6a28": "\"{0}\" {{key}} \"{1}\" inconnu.",
|
||||
"ba96498b10c179f9cd75f75c8def4f70": "{{realm}} est obligatoire",
|
||||
"c0057a569ff9d3b509bac61a4b2f605d": "Supprime tous les {0} de ce modèle.",
|
||||
"c2b5d51f007178170ca3952d59640ca4": "Impossible de rectifier les modifications {0} :\n{1}",
|
||||
"c4ee6d177c974532c3552d2f98eb72ea": "Le middleware {0} a été supprimé dans la version 3.0. Pour plus de détails, voir {1}.",
|
||||
"c61a5a02ba3801a892308f70f5d55a14": "Les métadonnées Remoting {{\"isStatic\"}} sont obsolètes. Spécifiez {{\"prototype.name\"}} dans le nom de la méthode plutôt pour {{isStatic=false}}.",
|
||||
"c68a93f0a9524fed4ff64372fc90c55f": "Obligation de fournir une adresse électronique valide",
|
||||
"cd0412f2f33a4a2a316acc834f3f21a6": "obligation de spécifier {{id}} ou {{data}}",
|
||||
"d5552322de5605c58b62f47ad26d2716": "{{`app.boot`}} a été supprimé ; utilisez à la place le nouveau module {{loopback-boot}}",
|
||||
"d6f43b266533b04d442bdb3955622592": "Crée une instance dans {0} de ce modèle.",
|
||||
"da13d3cdf21330557254670dddd8c5c7": "Compte {0} de {1}.",
|
||||
"dc568bee32deb0f6eaf63e73b20e8ceb": "Le paramètre \"methods\" non objet de \"{0}\" est ignoré.",
|
||||
"e4434de4bb8f5a3cd1d416e4d80d7e0b": "\"{0}\" {{id}} \"{1}\" inconnu.",
|
||||
"e92aa25b6b864e3454b65a7c422bd114": "La mise à jour en bloc a échoué ; le connecteur a supprimé un nombre inattendu d'enregistrements : {0}",
|
||||
|
@ -71,7 +87,7 @@
|
|||
"f0aed00a3d3d0b97d6594e4b70e0c201": "\t TRANSPORT :{0}",
|
||||
"f0bd73df8714cefb925e3b8da2f4c5f6": "résultat :{0}",
|
||||
"f1d4ac54357cc0932f385d56814ba7e4": "Conflit",
|
||||
"f58cdc481540cd1f69a4aa4da2e37981": "Mot de passe non valide : {0}",
|
||||
"f66ae3cf379b2fce28575a3282defe1a": "Supprime {0} de ce modèle.",
|
||||
"f8e26bcca62a47f579562f1cd2c785ff": "Transformez votre application pour utiliser la solution officielle d'injection de l'argument \"options\" à partir du contexte de demande. \nVoir {0}"
|
||||
}
|
||||
|
||||
|
|
|
@ -3,11 +3,13 @@
|
|||
"04bd8af876f001ceaf443aad6a9002f9": "L'autenticazione richiede che sia definito il modello {0}.",
|
||||
"095afbf2f1f0e5be678f5dac5c54e717": "Accesso negato",
|
||||
"0caffe1d763c8cca6a61814abe33b776": "L'email è obbligatoria",
|
||||
"0e21aad369dd09e1965c11949303cefd": "Metadati della comunicazione in remoto per {0}.{1} {{\"isStatic\"}} non corrisponde al nuovo stile basato sul nome del metodo.",
|
||||
"0da38687fed24275c1547e815914a8e3": "Eliminare un elemento correlato in base all'ID per {0}.",
|
||||
"0e21aad369dd09e1965c11949303cefd": "L'esecuzione della copia in remoto dei metadata per {0}.{1} {{\"isStatic\"}} non corrisponde al nuovo stile basato sul nome del metodo.",
|
||||
"10e01c895dc0b2fecc385f9f462f1ca6": "un elenco dei colori è disponibile all'indirizzo {{http://localhost:3000/colors}}",
|
||||
"1b2a6076dccbe91a56f1672eb3b8598c": "Il corpo della risposta contiene proprietà del {{AccessToken}} creato all'accesso.\nIn base al valore del parametro `include`, il corpo può contenere ulteriori proprietà:\n\n - `user` - `U+007BUserU+007D` - Dati dell'utente attualmente collegato.. {{(`include=user`)}}\n\n",
|
||||
"1d7833c3ca2f05fdad8fad7537531c40": "\t OGGETTO:{0}",
|
||||
"1e85f822b547a75d7d385048030e4ecb": "Creato: {0}",
|
||||
"22fe62fa8d595b72c62208beddaa2a56": "Aggiornare un elemento correlato in base all'ID per {0}.",
|
||||
"275f22ab95671f095640ca99194b7635": "\t DA:{0}",
|
||||
"2860bccdf9ef1e350c1a38932ed12173": "{0} è stato rimosso nella versione 3.0. Consultare {1} per ulteriori dettagli.",
|
||||
"2d3071e3b18681c80a090dc0efbdb349": "impossibile trovare {0} con id {1}",
|
||||
|
@ -16,62 +18,76 @@
|
|||
"3438fab56cc7ab92dfd88f0497e523e0": "La proprietà relations della configurazione `{0}` deve essere un oggetto",
|
||||
"3591f1d3e115b46f9f195df5ca548a6a": "Modello non trovato: il modello `{0}` estende un modello sconosciuto `{1}`.",
|
||||
"35e5252c62d80f8c54a5290d30f4c7d0": "Verificare la e-mail aprendo questo link in un browser web:\n\t{0}",
|
||||
"37bcd4b50dfae98734772e39ffb1ea3d": "La password immessa è troppo lunga. La lunghezza massima è {0} (immessa {1})",
|
||||
"3aae63bb7e8e046641767571c1591441": "login non riuscito perché l'email non è stata verificata",
|
||||
"3aecb24fa8bdd3f79d168761ca8a6729": "Fase {{middleware}} sconosciuta {0}",
|
||||
"3ca45aa6f705c46a4c598a900716f086": "{0} utilizza l'impostazione del modello {1} che non è più disponibile. ",
|
||||
"3ca45aa6f705c46a4c598a900716f086": "{0} utilizza l'impostazione del modello {1} che non è più disponibile.",
|
||||
"3caaa84fc103d6d5612173ae6d43b245": "Token non valido: {0}",
|
||||
"3d617953470be16d0c2b32f0bcfbb5ee": "Grazie per essersi registrati",
|
||||
"3d63008ccfb2af1db2142e8cc2716ace": "Avvertenza: nessun trasporto email specificato per l'invio della email. Configurare un trasporto per inviare messaggi email.",
|
||||
"4203ab415ec66a78d3164345439ba76e": "Impossibile richiamare {0}.{1}(). Il metodo {2} non è stato configurato. {{PersistedModel}} non è stato correttamente collegato ad una {{DataSource}}!",
|
||||
"4203ab415ec66a78d3164345439ba76e": "Impossibile chiamare {0}.{1}(). Il metodo {2} non è stato configurato. {{PersistedModel}} non è stato correttamente collegato ad una {{DataSource}}!",
|
||||
"42a36bac5cf03c4418d664500c81047a": "L'opzione di {{DataSource}} {{\"defaultForType\"}} non è più supportata",
|
||||
"44a6c8b1ded4ed653d19ddeaaf89a606": "Email non trovata",
|
||||
"4a4f04a4e480fc5d4ee73b84d9a4b904": "Invio email:",
|
||||
"4b494de07f524703ac0879addbd64b13": "La e-mail non è stata verificata",
|
||||
"528325f3cbf1b0ab9a08447515daac9a": "Aggiornare {0} di questo modello.",
|
||||
"543d19bad5e47ee1e9eb8af688e857b4": "Chiave esterna per {0}.",
|
||||
"57b87ae0e65f6ab7a2e3e6cbdfca49a4": "Impossibile creare l'origine dati {0}: {1}",
|
||||
"5858e63efaa0e4ad86b61c0459ea32fa": "È necessario collegare il modello {{Email}} ad un connettore {{Mail}}",
|
||||
"598ff0255ffd1d1b71e8de55dbe2c034": "Verificare l'esistenza della relazione {0} ad un elemento in base all'ID.",
|
||||
"5a36cc6ba0cc27c754f6c5ed6015ea3c": "Rimuovere la relazione {0} ad un elemento in base all'ID.",
|
||||
"5e81ad3847a290dc650b47618b9cbc7e": "login non riuscito",
|
||||
"5fa3afb425819ebde958043e598cb664": "impossibile trovare un modello con {{id}} {0}",
|
||||
"61e5deebaf44d68f4e6a508f30cc31a3": "La relazione `{0}` non esiste per il modello `{1}`",
|
||||
"62e8b0a733417978bab22c8dacf5d7e6": "Impossibile applicare gli aggiornamenti in massa, il connettore non indica correttamente il numero di record aggiornati.",
|
||||
"63a091ced88001ab6acb58f61ec041c5": "\t TESTO:{0}",
|
||||
"651f0b3cbba001635152ec3d3d954d0a": "Trovare un elemento correlato in base all'ID per {0}.",
|
||||
"6bc376432cd9972cf991aad3de371e78": "Dati mancanti per la modifica: {0}",
|
||||
"705c2d456a3e204c4af56e671ec3225c": "Impossibile trovare {{accessToken}}",
|
||||
"734a7bebb65e10899935126ba63dd51f": "La proprietà options della configurazione `{0}` deve essere un oggetto",
|
||||
"779467f467862836e19f494a37d6ab77": "La proprietà acls della configurazione `{0}` deve essere un array di oggetti",
|
||||
"7bc7b301ad9c4fc873029d57fb9740fe": "Query {0} di {1}.",
|
||||
"7c837b88fd0e509bd3fc722d7ddf0711": "Chiave esterna per {0}",
|
||||
"7d5e7ed0efaedf3f55f380caae0df8b8": "Prima applicazione mobile personale",
|
||||
"7e0fca41d098607e1c9aa353c67e0fa1": "Token di accesso non valido",
|
||||
"7e287fc885d9fdcf42da3a12f38572c1": "Autorizzazione richiesta",
|
||||
"7ea04ea91aac3cb7ce0ddd96b7ff1fa4": "{{accessToken}} deve scollegarsi",
|
||||
"80a32e80cbed65eba2103201a7c94710": "Modello non trovato: {0}",
|
||||
"830cb6c862f8f364e9064cea0026f701": "Recupera la relazione hasOne {0}.",
|
||||
"83cbdc2560ba9f09155ccfc63e08f1a1": "Impossibile riconfigurare la proprietà `{0}` per `{1}`",
|
||||
"855eb8db89b4921c42072832d33d2dc2": "Password non valida.",
|
||||
"855ecd4a64885ba272d782435f72a4d4": "ID sconosciuto \"{0}\" \"{1}\".",
|
||||
"860d1a0b8bd340411fb32baa72867989": "Il trasporto non supporta i reindirizzamenti HTTP.",
|
||||
"86254879d01a60826a851066987703f2": "Aggiungere un elemento correlato in base all'ID per {0}.",
|
||||
"895b1f941d026870b3cc8e6af087c197": "Sono richiesti {{username}} o {{email}}",
|
||||
"8a17c5ef611e2e7535792316e66b8fca": "Password troppo lunga: {0}",
|
||||
"8ae418c605b6a45f2651be9b1677c180": "Metodo remoto non valido: `{0}`",
|
||||
"8bab6720ecc58ec6412358c858a53484": "Aggiornamento in massa non riuscito, il connettore ha modificato un numero non previsto di record: {0}",
|
||||
"8ecab7f534de38360bd1b1c88e880123": "I modelli child di `{0}` non erediteranno i metodi remoti definiti di recente {1}.",
|
||||
"93ba9a1d03da3b7696332d3f155c5bb7": "\t HTML:{0}",
|
||||
"97795efe0c3eb7f35ce8cf8cfe70682b": "La configurazione di `{0}` non contiene la proprietà {{`dataSource`}}.\nUtilizzare `null` o `false` per contrassegnare i modelli non collegati ad alcuna origine dati.",
|
||||
"9e3cbc1d5a9347cdcf6d1b4a6dbb55b7": "Recupera la relazione belongsTo {0}.",
|
||||
"a50d10fc6e0959b220e085454c40381e": "Utente non trovato: {0}",
|
||||
"b6f740aeb6f2eb9bee9cb049dbfe6a28": "{{key}} \"{0}\" sconosciuto \"{1}\".",
|
||||
"ba96498b10c179f9cd75f75c8def4f70": "{{realm}} è obbligatorio",
|
||||
"c0057a569ff9d3b509bac61a4b2f605d": "Elimina tutti i {0} di questo modello.",
|
||||
"c2b5d51f007178170ca3952d59640ca4": "Impossibile correggere {0} modifiche:\n{1}",
|
||||
"c4ee6d177c974532c3552d2f98eb72ea": "Middleware {0} è stato rimosso nella versione 3.0. Consultare {1} per ulteriori dettagli.",
|
||||
"c61a5a02ba3801a892308f70f5d55a14": "L'impostazione dei metadati {{\"isStatic\"}} in remoto è un'operazione obsoleta. Specificare {{\"prototype.name\"}} nel nome del metodo invece di {{isStatic=false}}.",
|
||||
"c68a93f0a9524fed4ff64372fc90c55f": "È necessario fornire una email valida",
|
||||
"cd0412f2f33a4a2a316acc834f3f21a6": "è necessario specificare {{id}} o {{data}}",
|
||||
"d5552322de5605c58b62f47ad26d2716": "{{`app.boot`}} è stato rimosso, utilizzare il nuovo modulo {{loopback-boot}}",
|
||||
"d6f43b266533b04d442bdb3955622592": "Crea una nuova istanza di questo modello in {0}.",
|
||||
"da13d3cdf21330557254670dddd8c5c7": "{0} di {1}.",
|
||||
"dc568bee32deb0f6eaf63e73b20e8ceb": "L'impostazione \"methods\" non oggetto di \"{0}\" viene ignorata.",
|
||||
"e4434de4bb8f5a3cd1d416e4d80d7e0b": "{{id}} \"{0}\" sconosciuto \"{1}\".",
|
||||
"e92aa25b6b864e3454b65a7c422bd114": "Aggiornamento in massa non riuscito, il connettore ha eliminato un numero non previsto di record: {0}",
|
||||
"ea63d226b6968e328bdf6876010786b5": "Impossibile applicare gli aggiornamenti in massa, il connettore non indica correttamente il numero di record eliminati.",
|
||||
"ead044e2b4bce74b4357f8a03fb78ec4": "Impossibile richiamare {0}.{1}(). Il metodo {2} non è stato configurato. {{KeyValueModel}} non è stato correttamente collegato ad una {{DataSource}}!",
|
||||
"ead044e2b4bce74b4357f8a03fb78ec4": "Impossibile chiamare {0}.{1}(). Il metodo {2} non è stato configurato. {{KeyValueModel}} non è stato correttamente collegato ad una {{DataSource}}!",
|
||||
"ecb06666ef95e5db27a5ac1d6a17923b": "\t A:{0}",
|
||||
"f0aed00a3d3d0b97d6594e4b70e0c201": "\t TRASPORTO:{0}",
|
||||
"f0bd73df8714cefb925e3b8da2f4c5f6": "risultato:{0}",
|
||||
"f1d4ac54357cc0932f385d56814ba7e4": "Conflitto",
|
||||
"f58cdc481540cd1f69a4aa4da2e37981": "Password non valida: {0}",
|
||||
"f66ae3cf379b2fce28575a3282defe1a": "Elimina {0} di questo modello.",
|
||||
"f8e26bcca62a47f579562f1cd2c785ff": "Impostare l'app in modo da utilizzare la soluzione ufficiale per inserire l'argomento \"options\" dal contesto della richiesta,\nconsultare {0}"
|
||||
}
|
||||
|
||||
|
|
|
@ -3,11 +3,13 @@
|
|||
"04bd8af876f001ceaf443aad6a9002f9": "認証では、モデル {0} を定義する必要があります。",
|
||||
"095afbf2f1f0e5be678f5dac5c54e717": "アクセス拒否",
|
||||
"0caffe1d763c8cca6a61814abe33b776": "E メールは必須です",
|
||||
"0da38687fed24275c1547e815914a8e3": "ID を指定して {0} の関連項目を削除します。",
|
||||
"0e21aad369dd09e1965c11949303cefd": "{0}.{1} のリモート・メタデータ {{\"isStatic\"}} は、新規メソッドの名前ベースの方式と一致しません。",
|
||||
"10e01c895dc0b2fecc385f9f462f1ca6": "カラー・リストは {{http://localhost:3000/colors}} で利用できます",
|
||||
"1b2a6076dccbe91a56f1672eb3b8598c": "応答本文には、ログイン時に作成された {{AccessToken}} のプロパティーが含まれます。\n`include` パラメーターの値によっては、本文に追加のプロパティーが含まれる場合があります:\n\n - `user` - `U+007BUserU+007D` - 現在ログインしているユーザーのデータ。 {{(`include=user`)}}\n\n",
|
||||
"1d7833c3ca2f05fdad8fad7537531c40": "\t 件名:{0}",
|
||||
"1e85f822b547a75d7d385048030e4ecb": "作成済み: {0}",
|
||||
"22fe62fa8d595b72c62208beddaa2a56": "ID を指定して {0} の関連項目を更新します。",
|
||||
"275f22ab95671f095640ca99194b7635": "\t 送信元:{0}",
|
||||
"2860bccdf9ef1e350c1a38932ed12173": "{0} は、バージョン 3.0 で削除されました。 詳しくは、{1} を参照してください。",
|
||||
"2d3071e3b18681c80a090dc0efbdb349": "ID {1} の {0} が見つかりませんでした",
|
||||
|
@ -16,6 +18,7 @@
|
|||
"3438fab56cc7ab92dfd88f0497e523e0": "`{0}` 構成の関係プロパティーはオブジェクトでなければなりません",
|
||||
"3591f1d3e115b46f9f195df5ca548a6a": "モデルが見つかりません: モデル `{0}` は不明のモデル `{1}` を拡張しています。",
|
||||
"35e5252c62d80f8c54a5290d30f4c7d0": "Web ブラウザーで次のリンクを開いて、E メールを検証してください: \n\t{0}",
|
||||
"37bcd4b50dfae98734772e39ffb1ea3d": "入力したパスワードが長すぎます。最大長は {0} です ({1} が入力されました)",
|
||||
"3aae63bb7e8e046641767571c1591441": "E メールが検証されていないため、ログインに失敗しました",
|
||||
"3aecb24fa8bdd3f79d168761ca8a6729": "不明な {{middleware}} フェーズ {0}",
|
||||
"3ca45aa6f705c46a4c598a900716f086": "{0} は、使用できなくなったモデル設定 {1} を使用しています。",
|
||||
|
@ -27,41 +30,54 @@
|
|||
"44a6c8b1ded4ed653d19ddeaaf89a606": "E メールが見つかりません",
|
||||
"4a4f04a4e480fc5d4ee73b84d9a4b904": "メールの送信:",
|
||||
"4b494de07f524703ac0879addbd64b13": "E メールが検証されていません",
|
||||
"528325f3cbf1b0ab9a08447515daac9a": "このモデルの {0} を更新します。",
|
||||
"543d19bad5e47ee1e9eb8af688e857b4": "{0} の外部キー。",
|
||||
"57b87ae0e65f6ab7a2e3e6cbdfca49a4": "データ・ソース {0}: {1} を作成できません",
|
||||
"5858e63efaa0e4ad86b61c0459ea32fa": "{{Email}} モデルを {{Mail}} コネクターに接続する必要があります",
|
||||
"598ff0255ffd1d1b71e8de55dbe2c034": "ID を指定して項目との {0} 関係があることを確認します。",
|
||||
"5a36cc6ba0cc27c754f6c5ed6015ea3c": "ID を指定して項目との {0} 関係を削除します。",
|
||||
"5e81ad3847a290dc650b47618b9cbc7e": "ログインに失敗しました",
|
||||
"5fa3afb425819ebde958043e598cb664": "{{id}} {0} のモデルが見つかりませんでした",
|
||||
"61e5deebaf44d68f4e6a508f30cc31a3": "モデル `{1}` には関係 `{0}` が存在しません",
|
||||
"62e8b0a733417978bab22c8dacf5d7e6": "一括更新を適用できません。コネクターは更新されたレコードの数を正しく報告していません。",
|
||||
"63a091ced88001ab6acb58f61ec041c5": "\t テキスト:{0}",
|
||||
"651f0b3cbba001635152ec3d3d954d0a": "ID を指定して {0} の関連項目を検索します。",
|
||||
"6bc376432cd9972cf991aad3de371e78": "変更用のデータがありません: {0}",
|
||||
"705c2d456a3e204c4af56e671ec3225c": "{{accessToken}} が見つかりませんでした",
|
||||
"734a7bebb65e10899935126ba63dd51f": "`{0}` 構成のオプション・プロパティーはオブジェクトでなければなりません",
|
||||
"779467f467862836e19f494a37d6ab77": "`{0}` 構成の ACL プロパティーはオブジェクトの配列でなければなりません",
|
||||
"7bc7b301ad9c4fc873029d57fb9740fe": "{1} の {0} を照会します。",
|
||||
"7c837b88fd0e509bd3fc722d7ddf0711": "{0} の外部キー",
|
||||
"7d5e7ed0efaedf3f55f380caae0df8b8": "最初のモバイル・アプリケーション",
|
||||
"7e0fca41d098607e1c9aa353c67e0fa1": "無効なアクセス・トークン",
|
||||
"7e287fc885d9fdcf42da3a12f38572c1": "許可が必要です",
|
||||
"7ea04ea91aac3cb7ce0ddd96b7ff1fa4": "ログアウトするには {{accessToken}} が必要です",
|
||||
"80a32e80cbed65eba2103201a7c94710": "モデルが見つかりません: {0}",
|
||||
"830cb6c862f8f364e9064cea0026f701": "hasOne 関係 {0} をフェッチします。",
|
||||
"83cbdc2560ba9f09155ccfc63e08f1a1": "`{1}` のプロパティー `{0}` を再構成できません",
|
||||
"855eb8db89b4921c42072832d33d2dc2": "パスワードが無効です。",
|
||||
"855ecd4a64885ba272d782435f72a4d4": "\"{0}\" ID \"{1}\" が不明です。",
|
||||
"860d1a0b8bd340411fb32baa72867989": "トランスポートでは HTTP リダイレクトはサポートされません。",
|
||||
"86254879d01a60826a851066987703f2": "ID を指定して {0} の関連項目を追加します。",
|
||||
"895b1f941d026870b3cc8e6af087c197": "{{username}} または {{email}} が必要です",
|
||||
"8a17c5ef611e2e7535792316e66b8fca": "パスワードが長すぎます: {0}",
|
||||
"8ae418c605b6a45f2651be9b1677c180": "無効なリモート・メソッド: `{0}`",
|
||||
"8bab6720ecc58ec6412358c858a53484": "一括更新が失敗しました。コネクターは予期しない数のレコードを変更しました: {0}",
|
||||
"8ecab7f534de38360bd1b1c88e880123": "`{0}` の子モデルは、新しく定義されたリモート・メソッド {1} を継承しません。",
|
||||
"93ba9a1d03da3b7696332d3f155c5bb7": "\t HTML:{0}",
|
||||
"97795efe0c3eb7f35ce8cf8cfe70682b": "`{0}` の構成は {{`dataSource`}} プロパティーがありません。\nどのデータ・ソースにも付加されていないモデルにマークを付けるには `null` または `false` を使用します。",
|
||||
"9e3cbc1d5a9347cdcf6d1b4a6dbb55b7": "belongsTo 関係 {0} をフェッチします。",
|
||||
"a50d10fc6e0959b220e085454c40381e": "ユーザーが見つかりません: {0}",
|
||||
"b6f740aeb6f2eb9bee9cb049dbfe6a28": "\"{0}\" {{key}} \"{1}\" が不明です。",
|
||||
"ba96498b10c179f9cd75f75c8def4f70": "{{realm}} は必須です",
|
||||
"c0057a569ff9d3b509bac61a4b2f605d": "このモデルのすべての {0} を削除します。",
|
||||
"c2b5d51f007178170ca3952d59640ca4": "{0} の変更を修正できません:\n{1}",
|
||||
"c4ee6d177c974532c3552d2f98eb72ea": "{0} ミドルウェアは、バージョン 3.0 で削除されました。 詳しくは、{1} を参照してください。",
|
||||
"c61a5a02ba3801a892308f70f5d55a14": "リモート・メタデータ {{\"isStatic\"}} は非推奨です。 メソッド名では {{isStatic=false}} の代わりに {{\"prototype.name\"}} を指定してください。",
|
||||
"c68a93f0a9524fed4ff64372fc90c55f": "有効な E メールを指定する必要があります",
|
||||
"cd0412f2f33a4a2a316acc834f3f21a6": "{{id}} または {{data}} を指定する必要があります",
|
||||
"d5552322de5605c58b62f47ad26d2716": "{{`app.boot`}} は削除されました。代わりに新規モジュール {{loopback-boot}} を使用してください",
|
||||
"d6f43b266533b04d442bdb3955622592": "このモデルの {0} に新規インスタンスを作成します。",
|
||||
"da13d3cdf21330557254670dddd8c5c7": "{1} の {0} をカウントします。",
|
||||
"dc568bee32deb0f6eaf63e73b20e8ceb": "\"{0}\" の非オブジェクト「メソッド」設定を無視します。",
|
||||
"e4434de4bb8f5a3cd1d416e4d80d7e0b": "\"{0}\" {{id}} \"{1}\" が不明です。",
|
||||
"e92aa25b6b864e3454b65a7c422bd114": "一括更新が失敗しました。コネクターは予期しない数のレコードを削除しました: {0}",
|
||||
|
@ -71,7 +87,7 @@
|
|||
"f0aed00a3d3d0b97d6594e4b70e0c201": "\t トランスポート:{0}",
|
||||
"f0bd73df8714cefb925e3b8da2f4c5f6": "結果:{0}",
|
||||
"f1d4ac54357cc0932f385d56814ba7e4": "競合",
|
||||
"f58cdc481540cd1f69a4aa4da2e37981": "無効なパスワード: {0}",
|
||||
"f66ae3cf379b2fce28575a3282defe1a": "このモデルの {0} を削除します。",
|
||||
"f8e26bcca62a47f579562f1cd2c785ff": "要求コンテキストからの \"options\" 引数を注入するための公式な解決策を使用するようにアプリケーションを作り直してください。\n{0} を参照してください。"
|
||||
}
|
||||
|
||||
|
|
|
@ -3,11 +3,13 @@
|
|||
"04bd8af876f001ceaf443aad6a9002f9": "인증을 위해 {0} 모델이 정의되어야 함",
|
||||
"095afbf2f1f0e5be678f5dac5c54e717": "액세스 거부",
|
||||
"0caffe1d763c8cca6a61814abe33b776": "이메일은 필수입니다.",
|
||||
"0da38687fed24275c1547e815914a8e3": "{0}에 대해 ID로 관련 항목을 삭제하십시오.",
|
||||
"0e21aad369dd09e1965c11949303cefd": "{0}.{1} {{\"isStatic\"}}의 원격 메타데이터가 새 메소드 이름 기반 스타일과 일치하지 않습니다.",
|
||||
"10e01c895dc0b2fecc385f9f462f1ca6": "색상 목록은 {{http://localhost:3000/colors}}에 있음",
|
||||
"1b2a6076dccbe91a56f1672eb3b8598c": "응답 본문에 로그인 시 작성한 {{AccessToken}} 특성이 포함됩니다.\n`include` 매개변수 값에 따라 본문에 추가 특성이 포함될 수 있습니다. \n\n - `user` - `U+007BUserU+007D` - 현재 로그인된 사용자의 데이터. {{(`include=user`)}}\n\n",
|
||||
"1d7833c3ca2f05fdad8fad7537531c40": "\t 제목:{0}",
|
||||
"1e85f822b547a75d7d385048030e4ecb": "작성 날짜: {0}",
|
||||
"22fe62fa8d595b72c62208beddaa2a56": "{0}에 대해 ID로 관련 항목을 업데이트하십시오.",
|
||||
"275f22ab95671f095640ca99194b7635": "\t 발신인:{0}",
|
||||
"2860bccdf9ef1e350c1a38932ed12173": "버전 3.0에서 {0}이(가) 제거되었습니다. 자세한 정보는 {1}을(를) 참조하십시오.",
|
||||
"2d3071e3b18681c80a090dc0efbdb349": "ID {1}(으)로 {0}을(를) 찾을 수 없음",
|
||||
|
@ -16,6 +18,7 @@
|
|||
"3438fab56cc7ab92dfd88f0497e523e0": "`{0}` 구성의 관계 특성은 오브젝트여야 함",
|
||||
"3591f1d3e115b46f9f195df5ca548a6a": "모델을 찾을 수 없음: 모델 `{0}`은(는) 알 수 없는 모델 `{1}`의 확장입니다.",
|
||||
"35e5252c62d80f8c54a5290d30f4c7d0": "웹 브라우저에서 이 링크를 열어 이메일을 확인하십시오.\n\t{0}",
|
||||
"37bcd4b50dfae98734772e39ffb1ea3d": "입력한 비밀번호가 너무 깁니다. 최대 길이는 {0}입니다({1}자 입력함).",
|
||||
"3aae63bb7e8e046641767571c1591441": "이메일이 확인되지 않아서 로그인에 실패했습니다.",
|
||||
"3aecb24fa8bdd3f79d168761ca8a6729": "알 수 없는 {{middleware}} 단계 {0}",
|
||||
"3ca45aa6f705c46a4c598a900716f086": "{0}에서 더 이상 사용할 수 없는 모델 설정 {1}을(를) 사용하고 있습니다.",
|
||||
|
@ -27,41 +30,54 @@
|
|||
"44a6c8b1ded4ed653d19ddeaaf89a606": "이메일을 찾을 수 없음",
|
||||
"4a4f04a4e480fc5d4ee73b84d9a4b904": "메일 발송 중:",
|
||||
"4b494de07f524703ac0879addbd64b13": "이메일이 확인되지 않았습니다.",
|
||||
"528325f3cbf1b0ab9a08447515daac9a": "이 모델의 {0}을(를) 업데이트하십시오.",
|
||||
"543d19bad5e47ee1e9eb8af688e857b4": "{0}의 외부 키입니다.",
|
||||
"57b87ae0e65f6ab7a2e3e6cbdfca49a4": "데이터 소스 {0}을(를) 작성할 수 없음: {1}",
|
||||
"5858e63efaa0e4ad86b61c0459ea32fa": "{{Email}} 모델을 {{Mail}} 커넥터에 연결해야 합니다.",
|
||||
"598ff0255ffd1d1b71e8de55dbe2c034": "ID로 항목에 대한 {0} 관계의 존재를 확인하십시오.",
|
||||
"5a36cc6ba0cc27c754f6c5ed6015ea3c": "ID로 항목에 대한 {0} 관계를 제거하십시오.",
|
||||
"5e81ad3847a290dc650b47618b9cbc7e": "로그인 실패",
|
||||
"5fa3afb425819ebde958043e598cb664": "{{id}} {0}인 모델을 찾을 수 없음",
|
||||
"61e5deebaf44d68f4e6a508f30cc31a3": "모델 `{1}`에 대해 관계 `{0}`이(가) 없습니다.",
|
||||
"62e8b0a733417978bab22c8dacf5d7e6": "벌크 업데이트를 적용할 수 없습니다. 커넥터가 업데이트된 레코드 수를 제대로 보고하지 않습니다.",
|
||||
"63a091ced88001ab6acb58f61ec041c5": "\t 텍스트:{0}",
|
||||
"651f0b3cbba001635152ec3d3d954d0a": "{0}에 대해 ID로 관련 항목을 찾으십시오.",
|
||||
"6bc376432cd9972cf991aad3de371e78": "변경을 위한 데이터 누락: {0}",
|
||||
"705c2d456a3e204c4af56e671ec3225c": "{{accessToken}}을(를) 찾을 수 없음",
|
||||
"734a7bebb65e10899935126ba63dd51f": "`{0}` 구성의 옵션 특성은 오브젝트여야 함",
|
||||
"779467f467862836e19f494a37d6ab77": "`{0}` 구성의 acls 특성은 오브젝트 배열이어야 함",
|
||||
"7bc7b301ad9c4fc873029d57fb9740fe": "{1}의 {0}을(를) 조회합니다.",
|
||||
"7c837b88fd0e509bd3fc722d7ddf0711": "{0}의 외부 키",
|
||||
"7d5e7ed0efaedf3f55f380caae0df8b8": "내 첫 번째 모바일 애플리케이션",
|
||||
"7e0fca41d098607e1c9aa353c67e0fa1": "올바르지 않은 액세스 토큰",
|
||||
"7e287fc885d9fdcf42da3a12f38572c1": "권한 필수",
|
||||
"7ea04ea91aac3cb7ce0ddd96b7ff1fa4": "{{accessToken}}이(가) 로그아웃해야 함",
|
||||
"80a32e80cbed65eba2103201a7c94710": "모델을 찾을 수 없음: {0}",
|
||||
"830cb6c862f8f364e9064cea0026f701": "페치에 하나의 관계 {0}이(가) 있습니다.",
|
||||
"83cbdc2560ba9f09155ccfc63e08f1a1": "`{1}`에 대해 `{0}` 특성을 다시 구성할 수 없음",
|
||||
"855eb8db89b4921c42072832d33d2dc2": "올바르지 않은 비밀번호입니다.",
|
||||
"855ecd4a64885ba272d782435f72a4d4": "알 수 없는 \"{0}\" ID \"{1}\".",
|
||||
"860d1a0b8bd340411fb32baa72867989": "전송에서 HTTP 경로 재지원을 지원하지 않습니다.",
|
||||
"86254879d01a60826a851066987703f2": "{0}에 대해 ID로 관련 항목을 추가하십시오.",
|
||||
"895b1f941d026870b3cc8e6af087c197": "{{username}} 또는 {{email}}은(는) 필수입니다.",
|
||||
"8a17c5ef611e2e7535792316e66b8fca": "비밀번호가 너무 김: {0}",
|
||||
"8ae418c605b6a45f2651be9b1677c180": "올바르지 않은 원격 메소드: `{0}`",
|
||||
"8bab6720ecc58ec6412358c858a53484": "벌크 업데이트에 실패했습니다. 커넥터가 예상치 못한 수의 레코드를 수정했습니다. {0}",
|
||||
"8ecab7f534de38360bd1b1c88e880123": "'{0}'의 하위 모델은 새로 정의된 원격 메소드 {1}을(를) 상속하지 않습니다.",
|
||||
"93ba9a1d03da3b7696332d3f155c5bb7": "\t HTML:{0}",
|
||||
"97795efe0c3eb7f35ce8cf8cfe70682b": "`{0}`의 구성에 {{`dataSource`}} 특성이 누락되었습니다.\n데이터 소스에 첨부되지 않은 모델을 표시하려면 `null` 또는 `false`를 사용하십시오.",
|
||||
"9e3cbc1d5a9347cdcf6d1b4a6dbb55b7": "페치가 관계 {0}에 속합니다.",
|
||||
"a50d10fc6e0959b220e085454c40381e": "사용자를 찾을 수 없음: {0}",
|
||||
"b6f740aeb6f2eb9bee9cb049dbfe6a28": "알 수 없는 \"{0}\" {{key}} \"{1}\".",
|
||||
"ba96498b10c179f9cd75f75c8def4f70": "{{realm}}은(는) 필수입니다.",
|
||||
"c0057a569ff9d3b509bac61a4b2f605d": "이 모델의 모든 {0}을(를) 삭제합니다.",
|
||||
"c2b5d51f007178170ca3952d59640ca4": "{0} 변경사항을 교정할 수 없음:\n{1}",
|
||||
"c4ee6d177c974532c3552d2f98eb72ea": "버전 3.0에서 {0} 미들웨어가 제거되었습니다. 자세한 정보는 {1}을(를) 참조하십시오.",
|
||||
"c61a5a02ba3801a892308f70f5d55a14": "원격 메타데이터 {{\"isStatic\"}}이(가) 더 이상 사용되지 않습니다. {{isStatic=false}}인 경우 메소드 이름에 대신 {{\"prototype.name\"}}을(를) 지정하십시오.",
|
||||
"c68a93f0a9524fed4ff64372fc90c55f": "올바른 이메일을 제공해야 함",
|
||||
"cd0412f2f33a4a2a316acc834f3f21a6": "{{id}} 또는 {{data}}을(를) 지정해야 함",
|
||||
"d5552322de5605c58b62f47ad26d2716": "{{`app.boot`}}이(가) 제거되었습니다. 대신 새 모듈 {{loopback-boot}}을(를) 사용하십시오.",
|
||||
"d6f43b266533b04d442bdb3955622592": "이 모델의 {0}에서 새 인스턴스를 작성합니다.",
|
||||
"da13d3cdf21330557254670dddd8c5c7": "{1}의 {0}을(를) 계수합니다.",
|
||||
"dc568bee32deb0f6eaf63e73b20e8ceb": "\"{0}\"의 비오브젝트 \"methods\" 설정 무시",
|
||||
"e4434de4bb8f5a3cd1d416e4d80d7e0b": "알 수 없는 \"{0}\" {{id}} \"{1}\".",
|
||||
"e92aa25b6b864e3454b65a7c422bd114": "벌크 업데이트에 실패했습니다. 커넥터가 예상치 못한 수의 레코드를 삭제했습니다. {0}",
|
||||
|
@ -71,7 +87,7 @@
|
|||
"f0aed00a3d3d0b97d6594e4b70e0c201": "\t 전송:{0}",
|
||||
"f0bd73df8714cefb925e3b8da2f4c5f6": "결과: {0}",
|
||||
"f1d4ac54357cc0932f385d56814ba7e4": "충돌",
|
||||
"f58cdc481540cd1f69a4aa4da2e37981": "올바르지 않은 비밀번호: {0}",
|
||||
"f66ae3cf379b2fce28575a3282defe1a": "이 모델의 {0}을(를) 삭제합니다.",
|
||||
"f8e26bcca62a47f579562f1cd2c785ff": "요청 컨텍스트의 \"options\" 인수 삽입에 정식 솔루션을 사용하도록 앱을 다시 작업하십시오.\n{0} 참조"
|
||||
}
|
||||
|
||||
|
|
|
@ -3,11 +3,13 @@
|
|||
"04bd8af876f001ceaf443aad6a9002f9": "Voor verificatie moet model {0} worden gedefinieerd.",
|
||||
"095afbf2f1f0e5be678f5dac5c54e717": "Toegang geweigerd",
|
||||
"0caffe1d763c8cca6a61814abe33b776": "E-mail is vereist",
|
||||
"0e21aad369dd09e1965c11949303cefd": "Remoting (externe) metagegevens voor {0}.{1} {{\"isStatic\"}} komen niet overeen met nieuwe naam-gebaseerde stijl van methode.",
|
||||
"0da38687fed24275c1547e815914a8e3": "Gerelateerd item wissen op basis van ID voor {0}.",
|
||||
"0e21aad369dd09e1965c11949303cefd": "Het niet-lokaal zetten van metagegevens voor {0}.{1} {{\"isStatic\"}} komt niet overeen met nieuwe op naam gebaseerde stijl van de methode.",
|
||||
"10e01c895dc0b2fecc385f9f462f1ca6": "een lijst van kleuren is beschikbaar op {{http://localhost:3000/colors}}",
|
||||
"1b2a6076dccbe91a56f1672eb3b8598c": "De lopende tekst van het antwoord bevat eigenschappen van het {{AccessToken}} dat is gemaakt bij aanmelding.\nAfhankelijk van de waarde van de parameter 'include' kan de lopende tekst aanvullende eigenschappen bevatten:\n\n - 'user' - 'U+007BUserU+007D' - Gegevens van de aangemelde gebruiker. {{(`include=user`)}}\n\n",
|
||||
"1d7833c3ca2f05fdad8fad7537531c40": "\t ONDERWERP: {0}",
|
||||
"1e85f822b547a75d7d385048030e4ecb": "Gemaakt: {0}",
|
||||
"22fe62fa8d595b72c62208beddaa2a56": "Gerelateerd item bijwerken op basis van ID voor {0}.",
|
||||
"275f22ab95671f095640ca99194b7635": "\t VAN: {0}",
|
||||
"2860bccdf9ef1e350c1a38932ed12173": "{0} is versie 3.0 verwijderd. Zie {1} voor meer informatie.",
|
||||
"2d3071e3b18681c80a090dc0efbdb349": "kan {0} met ID {1} niet vinden",
|
||||
|
@ -16,62 +18,76 @@
|
|||
"3438fab56cc7ab92dfd88f0497e523e0": "De relaties-eigenschap van de '{0}'-configuratie moet een object zijn",
|
||||
"3591f1d3e115b46f9f195df5ca548a6a": "Model niet gevonden: model '{0}' is een uitbreiding van onbekend model '{1}'.",
|
||||
"35e5252c62d80f8c54a5290d30f4c7d0": "Controleer uw e-mail door deze link te openen in een webbrowser:\n\t{0}",
|
||||
"37bcd4b50dfae98734772e39ffb1ea3d": "Het opgegeven wachtwoord is te lang. Max lengte is {0} (opgegeven {1})",
|
||||
"3aae63bb7e8e046641767571c1591441": "Aanmelding mislukt omdat e-mail niet is gecontroleerd",
|
||||
"3aecb24fa8bdd3f79d168761ca8a6729": "Onbekende {{middleware}}-fase {0}",
|
||||
"3ca45aa6f705c46a4c598a900716f086": "{0} werkt met modelinstelling {1}, maar deze is niet meer beschikbaar.",
|
||||
"3caaa84fc103d6d5612173ae6d43b245": "Ongeldig token: {0}",
|
||||
"3d617953470be16d0c2b32f0bcfbb5ee": "Hartelijk dank voor uw registratie",
|
||||
"3d63008ccfb2af1db2142e8cc2716ace": "Waarschuwing: Geen e-mailtransport opgegeven voor verzending van e-mail. Configureer een transport om e-mailberichten te verzenden.",
|
||||
"4203ab415ec66a78d3164345439ba76e": "{0} kan niet worden aangeroepen. {1}(). De methode {2} is niet geconfigureerd. De {{PersistedModel}} is niet correct gekoppeld aan een {{DataSource}}!",
|
||||
"4203ab415ec66a78d3164345439ba76e": "Kan {0}.{1}() niet aanroepen. De methode {2} is niet geconfigureerd. De {{PersistedModel}} is niet correct gekoppeld aan een {{DataSource}}!",
|
||||
"42a36bac5cf03c4418d664500c81047a": "{{DataSource}}-optie {{\"defaultForType\"}} wordt niet meer ondersteund",
|
||||
"44a6c8b1ded4ed653d19ddeaaf89a606": "E-mail is niet gevonden",
|
||||
"4a4f04a4e480fc5d4ee73b84d9a4b904": "Mail verzenden:",
|
||||
"4b494de07f524703ac0879addbd64b13": "E-mail is niet geverifieerd",
|
||||
"57b87ae0e65f6ab7a2e3e6cbdfca49a4": "Gegevensbron {0}: {1} kan n iet worden gemaakt",
|
||||
"528325f3cbf1b0ab9a08447515daac9a": "{0} van dit model bijwerken.",
|
||||
"543d19bad5e47ee1e9eb8af688e857b4": "Externe sleutel voor {0}.",
|
||||
"57b87ae0e65f6ab7a2e3e6cbdfca49a4": "Probleem bij maken van gegevensbron {0}: {1}",
|
||||
"5858e63efaa0e4ad86b61c0459ea32fa": "U moet verbinding maken tussen het model {{Email}} en een {{Mail}}-connector",
|
||||
"598ff0255ffd1d1b71e8de55dbe2c034": "Bestaan van {0}-relatie met item controleren op basis van ID.",
|
||||
"5a36cc6ba0cc27c754f6c5ed6015ea3c": "Verwijder de {0}-relatie met een item op basis van ID.",
|
||||
"5e81ad3847a290dc650b47618b9cbc7e": "Aanmelden is mislukt",
|
||||
"5fa3afb425819ebde958043e598cb664": "geen model gevonden met {{id}} {0}",
|
||||
"61e5deebaf44d68f4e6a508f30cc31a3": "Relatie '{0}' voor model '{1}' bestaat niet",
|
||||
"62e8b0a733417978bab22c8dacf5d7e6": "Bulkupdates kunnen niet worden toegepast, de connector meldt niet het juiste aantal bijgewerkte records.",
|
||||
"63a091ced88001ab6acb58f61ec041c5": "\t TEKST: {0}",
|
||||
"651f0b3cbba001635152ec3d3d954d0a": "Gerelateerd item zoeken op basis van ID voor {0}.",
|
||||
"6bc376432cd9972cf991aad3de371e78": "Ontbrekende gegevens voor wijziging: {0}",
|
||||
"705c2d456a3e204c4af56e671ec3225c": "{{accessToken}} is niet gevonden",
|
||||
"734a7bebb65e10899935126ba63dd51f": "De opties-eigenschap van de '{0}'-configuratie moet een object zijn",
|
||||
"779467f467862836e19f494a37d6ab77": "De acls-eigenschap van de '{0}'-configuratie moet een array objecten zijn",
|
||||
"7bc7b301ad9c4fc873029d57fb9740fe": "Query's {0} van {1}.",
|
||||
"7c837b88fd0e509bd3fc722d7ddf0711": "Externe sleutel voor {0}",
|
||||
"7d5e7ed0efaedf3f55f380caae0df8b8": "Mijn eerste mobiele toepassing",
|
||||
"7e0fca41d098607e1c9aa353c67e0fa1": "Ongeldig toegangstoken",
|
||||
"7e287fc885d9fdcf42da3a12f38572c1": "Verplichte verificatie",
|
||||
"7ea04ea91aac3cb7ce0ddd96b7ff1fa4": "{{accessToken}} is vereist voor afmelding",
|
||||
"80a32e80cbed65eba2103201a7c94710": "Model is niet gevonden: {0}",
|
||||
"830cb6c862f8f364e9064cea0026f701": "Haalt hasOne-relatie {0} op.",
|
||||
"83cbdc2560ba9f09155ccfc63e08f1a1": "Eigenschap '{0}' mag niet opnieuw worden geconfigureerd voor '{1}'",
|
||||
"855eb8db89b4921c42072832d33d2dc2": "Ongeldig wachtwoord.",
|
||||
"855ecd4a64885ba272d782435f72a4d4": "Onbekend \"{0}\"-ID \"{1}\".",
|
||||
"860d1a0b8bd340411fb32baa72867989": "Transport biedt geen ondersteuning voor HTTP-omleidingen.",
|
||||
"86254879d01a60826a851066987703f2": "Gerelateerd item toevoegen op basis van ID voor {0}.",
|
||||
"895b1f941d026870b3cc8e6af087c197": "{{username}} of {{email}} is verplicht",
|
||||
"8a17c5ef611e2e7535792316e66b8fca": "Wachtwoord is te lang: {0}",
|
||||
"8ae418c605b6a45f2651be9b1677c180": "Ongeldige niet-lokale methode: '{0}'",
|
||||
"8bab6720ecc58ec6412358c858a53484": "Bulkupdate is mislukt; connector heeft een onverwacht aantal records gewijzigd: {0}",
|
||||
"8ecab7f534de38360bd1b1c88e880123": "Onderliggende modellen van '{0}' nemen de nieuw gedefinieerde niet-lokale methoden {1} niet over.",
|
||||
"93ba9a1d03da3b7696332d3f155c5bb7": "\t HTML: {0}",
|
||||
"97795efe0c3eb7f35ce8cf8cfe70682b": "De eigenschap {{`dataSource`}} ontbreekt in de configuratie van '{0}'.\nGebruik 'null' of 'false' om modellen te markeren die niet gekoppeld zijn aan een gegevensbron.",
|
||||
"9e3cbc1d5a9347cdcf6d1b4a6dbb55b7": "Haalt belongsTo-relatie {0} op.",
|
||||
"a50d10fc6e0959b220e085454c40381e": "Gebruiker is niet gevonden: {0}",
|
||||
"b6f740aeb6f2eb9bee9cb049dbfe6a28": "Onbekend \"{0}\" {{key}} \"{1}\".",
|
||||
"ba96498b10c179f9cd75f75c8def4f70": "{{realm}} is verplicht",
|
||||
"c0057a569ff9d3b509bac61a4b2f605d": "Verwijdert alle {0} van dit model.",
|
||||
"c2b5d51f007178170ca3952d59640ca4": "Wijzigingen van {0} kunnen niet worden hersteld:\n{1}",
|
||||
"c4ee6d177c974532c3552d2f98eb72ea": "{0} middleware is versie 3.0 verwijderd. Zie {1} voor meer informatie.",
|
||||
"c61a5a02ba3801a892308f70f5d55a14": "Extern plaatsen (remoting) van metagegevens {{\"isStatic\"}} is gedeprecieerd. Geef {{\"prototype.name\"}} op in naam van methode in plaats van {{isStatic=false}}.",
|
||||
"c68a93f0a9524fed4ff64372fc90c55f": "U moet een geldig e-mailadres opgeven",
|
||||
"cd0412f2f33a4a2a316acc834f3f21a6": "U moet een {{id}} of {{data}} opgeven",
|
||||
"d5552322de5605c58b62f47ad26d2716": "{{`app.boot`}} is verwijderd; gebruik in plaats daarvan de nieuwe module {{loopback-boot}}",
|
||||
"d6f43b266533b04d442bdb3955622592": "Maakt een nieuwe instance in {0} van dit model.",
|
||||
"da13d3cdf21330557254670dddd8c5c7": "Aantal {0} van {1}.",
|
||||
"dc568bee32deb0f6eaf63e73b20e8ceb": "Niet-object \"methods\"-instelling \"{0}\" wordt genegeerd.",
|
||||
"e4434de4bb8f5a3cd1d416e4d80d7e0b": "Onbekend \"{0}\" {{id}} \"{1}\".",
|
||||
"e92aa25b6b864e3454b65a7c422bd114": "Bulkupdate is mislukt; connector heeft een onverwacht aantal records gewist: {0}",
|
||||
"ea63d226b6968e328bdf6876010786b5": "Bulkupdates kunnen niet worden toegepast, de connector meldt niet het juiste aantal gewiste records.",
|
||||
"ead044e2b4bce74b4357f8a03fb78ec4": "{0} kan niet worden aangeroepen. {1}(). De methode {2} is niet geconfigureerd. De {{KeyValueModel}} is niet correct gekoppeld aan een {{DataSource}}!",
|
||||
"ead044e2b4bce74b4357f8a03fb78ec4": "Kan {0}.{1}() niet aanroepen. De methode {2} is niet geconfigureerd. De {{KeyValueModel}} is niet correct gekoppeld aan een {{DataSource}}!",
|
||||
"ecb06666ef95e5db27a5ac1d6a17923b": "\t AAN: {0}",
|
||||
"f0aed00a3d3d0b97d6594e4b70e0c201": "\t TRANSPORT: {0}",
|
||||
"f0bd73df8714cefb925e3b8da2f4c5f6": "resultaat:{0}",
|
||||
"f1d4ac54357cc0932f385d56814ba7e4": "Conflict",
|
||||
"f58cdc481540cd1f69a4aa4da2e37981": "Ongeldige wachtwoord: {0}",
|
||||
"f66ae3cf379b2fce28575a3282defe1a": "Verwijdert {0} van dit model.",
|
||||
"f8e26bcca62a47f579562f1cd2c785ff": "Herzie uw app zodanig dat deze gebruikmaakt van de officiële oplossing voor het injecteren van het argument \"options\" vanuit de aanvraagcontext. \nZie {0}"
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,93 @@
|
|||
{
|
||||
"03f79fa268fe199de2ce4345515431c1": "Nie znaleziono rekordu zmiany dla elementu {0} o identyfikatorze {1}",
|
||||
"04bd8af876f001ceaf443aad6a9002f9": "Uwierzytelnianie wymaga zdefiniowania modelu {0}.",
|
||||
"095afbf2f1f0e5be678f5dac5c54e717": "Odmowa dostępu",
|
||||
"0caffe1d763c8cca6a61814abe33b776": "Adres e-mail jest wymagany",
|
||||
"0da38687fed24275c1547e815914a8e3": "Usuń pokrewny element wg identyfikatora dla {0}.",
|
||||
"0e21aad369dd09e1965c11949303cefd": "Zdalne metadane dla {0}.{1} {{\"isStatic\"}} nie pasują do opartego na nazwie stylu nowej metody.",
|
||||
"10e01c895dc0b2fecc385f9f462f1ca6": "lista kolorów jest dostępna pod adresem {{http://localhost:3000/colors}}",
|
||||
"1b2a6076dccbe91a56f1672eb3b8598c": "Treść odpowiedzi zawiera właściwości elementu {{AccessToken}} utworzonego przy logowaniu.\nW zależności od wartości parametru `include`, treść może zawierać dodatkowe właściwości:\n\n - `user` - `U+007BUserU+007D` — dane aktualnie zalogowanego użytkownika. {{(`include=user`)}}\n\n",
|
||||
"1d7833c3ca2f05fdad8fad7537531c40": "\t TEMAT:{0}",
|
||||
"1e85f822b547a75d7d385048030e4ecb": "Utworzono: {0}",
|
||||
"22fe62fa8d595b72c62208beddaa2a56": "Zaktualizuj pokrewny element wg identyfikatora dla {0}.",
|
||||
"275f22ab95671f095640ca99194b7635": "\t OD:{0}",
|
||||
"2860bccdf9ef1e350c1a38932ed12173": "Element {0} został usunięty w wersji 3.0. Więcej informacji na ten temat zawiera sekcja {1}.",
|
||||
"2d3071e3b18681c80a090dc0efbdb349": "nie można znaleźć elementu {0} o identyfikatorze {1}",
|
||||
"316e5b82c203cf3de31a449ee07d0650": "Oczekiwano wartości boolowskiej, otrzymano {0}",
|
||||
"320c482401afa1207c04343ab162e803": "Niepoprawny typ elementu głównego: {0}",
|
||||
"3438fab56cc7ab92dfd88f0497e523e0": "Właściwość relacji konfiguracji `{0}` musi być obiektem",
|
||||
"3591f1d3e115b46f9f195df5ca548a6a": "Nie znaleziono modelu: model `{0}` rozszerza nieznany model `{1}`.",
|
||||
"35e5252c62d80f8c54a5290d30f4c7d0": "Zweryfikuj swój adres e-mail, otwierając ten odsyłacz w przeglądarce WWW:\n\t{0}",
|
||||
"37bcd4b50dfae98734772e39ffb1ea3d": "Wprowadzone hasło było zbyt długie. Maksymalna liczba znaków: {0} (wprowadzono: {1}).",
|
||||
"3aae63bb7e8e046641767571c1591441": "logowanie nie powiodło się, ponieważ adres e-mail nie został zweryfikowany",
|
||||
"3aecb24fa8bdd3f79d168761ca8a6729": "Nieznana faza {{middleware}} {0}",
|
||||
"3ca45aa6f705c46a4c598a900716f086": "{0} używa ustawienia modelu {1}, które nie jest już dostępne.",
|
||||
"3caaa84fc103d6d5612173ae6d43b245": "Niepoprawny znacznik: {0}",
|
||||
"3d617953470be16d0c2b32f0bcfbb5ee": "Dziękujemy za zarejestrowanie",
|
||||
"3d63008ccfb2af1db2142e8cc2716ace": "Ostrzeżenie: nie określono transportu poczty elektronicznej na potrzeby wysyłania wiadomości e-mail. Skonfiguruj transport w celu wysyłania wiadomości e-mail.",
|
||||
"4203ab415ec66a78d3164345439ba76e": "Nie można wywołać metody {0}.{1}(). Metoda {2} nie została skonfigurowana. Model {{PersistedModel}} nie został poprawnie przyłączony do źródła danych {{DataSource}}!",
|
||||
"42a36bac5cf03c4418d664500c81047a": "Opcja {{\"defaultForType\"}} źródła danych {{DataSource}} nie jest już obsługiwana",
|
||||
"44a6c8b1ded4ed653d19ddeaaf89a606": "Nie znaleziono adresu e-mail",
|
||||
"4a4f04a4e480fc5d4ee73b84d9a4b904": "Wysyłanie poczty:",
|
||||
"4b494de07f524703ac0879addbd64b13": "Adres e-mail nie został zweryfikowany",
|
||||
"528325f3cbf1b0ab9a08447515daac9a": "Zaktualizuj element {0} tego modelu.",
|
||||
"543d19bad5e47ee1e9eb8af688e857b4": "Klucz obcy dla {0}.",
|
||||
"57b87ae0e65f6ab7a2e3e6cbdfca49a4": "Nie można utworzyć źródła danych {0}: {1}",
|
||||
"5858e63efaa0e4ad86b61c0459ea32fa": "Należy połączyć model {{Email}} z konektorem {{Mail}}",
|
||||
"598ff0255ffd1d1b71e8de55dbe2c034": "Sprawdź istnienie relacji {0} z elementem wg identyfikatora.",
|
||||
"5a36cc6ba0cc27c754f6c5ed6015ea3c": "Usuń relację {0} z elementem wg identyfikatora.",
|
||||
"5e81ad3847a290dc650b47618b9cbc7e": "logowanie nie powiodło się",
|
||||
"5fa3afb425819ebde958043e598cb664": "nie można znaleźć modelu o identyfikatorze {{id}} {0}",
|
||||
"61e5deebaf44d68f4e6a508f30cc31a3": "Relacja `{0}` nie istnieje dla modelu `{1}'",
|
||||
"62e8b0a733417978bab22c8dacf5d7e6": "Nie można zastosować aktualizacji masowej, ponieważ konektor nie raportuje poprawnie liczby zaktualizowanych rekordów.",
|
||||
"63a091ced88001ab6acb58f61ec041c5": "\t TEKST:{0}",
|
||||
"651f0b3cbba001635152ec3d3d954d0a": "Znajdź pokrewny element wg identyfikatora dla {0}.",
|
||||
"6bc376432cd9972cf991aad3de371e78": "Brak danych do zmiany: {0}",
|
||||
"705c2d456a3e204c4af56e671ec3225c": "Nie można znaleźć obiektu {{accessToken}}",
|
||||
"734a7bebb65e10899935126ba63dd51f": "Właściwość options konfiguracji `{0}` musi być obiektem",
|
||||
"779467f467862836e19f494a37d6ab77": "Właściwość acls konfiguracji `{0}` musi być tablicą obiektów",
|
||||
"7bc7b301ad9c4fc873029d57fb9740fe": "Odpytuje element {0} w {1}.",
|
||||
"7c837b88fd0e509bd3fc722d7ddf0711": "Klucz obcy dla {0}",
|
||||
"7d5e7ed0efaedf3f55f380caae0df8b8": "Moja pierwsza aplikacja dla urządzeń przenośnych",
|
||||
"7e0fca41d098607e1c9aa353c67e0fa1": "Niepoprawny znacznik dostępu",
|
||||
"7e287fc885d9fdcf42da3a12f38572c1": "Wymagana autoryzacja",
|
||||
"7ea04ea91aac3cb7ce0ddd96b7ff1fa4": "{{accessToken}} jest wymagany do wylogowania się",
|
||||
"80a32e80cbed65eba2103201a7c94710": "Nie znaleziono modelu: {0}",
|
||||
"830cb6c862f8f364e9064cea0026f701": "Pobiera relację hasOne {0}.",
|
||||
"83cbdc2560ba9f09155ccfc63e08f1a1": "Nie można zrekonfigurować właściwości `{0}` dla `{1}`",
|
||||
"855eb8db89b4921c42072832d33d2dc2": "Niepoprawne hasło.",
|
||||
"855ecd4a64885ba272d782435f72a4d4": "Nieznany identyfikator \"{0}\" \"{1}\".",
|
||||
"860d1a0b8bd340411fb32baa72867989": "transport nie obsługuje przekierowań HTTP.",
|
||||
"86254879d01a60826a851066987703f2": "Dodaj pokrewny element wg identyfikatora dla {0}.",
|
||||
"895b1f941d026870b3cc8e6af087c197": "Wymagana jest {{username}} lub {{email}}",
|
||||
"8ae418c605b6a45f2651be9b1677c180": "Niepoprawna metoda zdalna: `{0}`",
|
||||
"8bab6720ecc58ec6412358c858a53484": "Aktualizacja masowa nie powiodła się, konektor zmodyfikował nieoczekiwaną liczbę rekordów: {0}",
|
||||
"8ecab7f534de38360bd1b1c88e880123": "Elementy potomne obiektu `{0}` nie odziedziczą nowo zdefiniowanych metod zdalnych {1}.",
|
||||
"93ba9a1d03da3b7696332d3f155c5bb7": "\t HTML:{0}",
|
||||
"97795efe0c3eb7f35ce8cf8cfe70682b": "W konfiguracji elementu `{0}` brakuje właściwości {{`dataSource`}}.\nUżyj wartości `null`, aby oznaczyć modele, które nie są przyłączone do żadnego źródła danych.",
|
||||
"9e3cbc1d5a9347cdcf6d1b4a6dbb55b7": "Pobiera relację belongsTo {0}.",
|
||||
"a50d10fc6e0959b220e085454c40381e": "Nie znaleziono użytkownika: {0}",
|
||||
"b6f740aeb6f2eb9bee9cb049dbfe6a28": "Nieznany klucz {{key}} \"{0}\" \"{1}\".",
|
||||
"ba96498b10c179f9cd75f75c8def4f70": "Właściwość {{realm}} jest wymagana.",
|
||||
"c0057a569ff9d3b509bac61a4b2f605d": "Usuwa wszystkie {0} tego modelu.",
|
||||
"c2b5d51f007178170ca3952d59640ca4": "Nie można skorygować {0} zmian: \n {1}",
|
||||
"c4ee6d177c974532c3552d2f98eb72ea": "Warstwa pośrednia {0} została usunięta w wersji 3.0. Więcej informacji na ten temat zawiera sekcja {1}.",
|
||||
"c61a5a02ba3801a892308f70f5d55a14": "Metadane zdalnego dostępu {{\"isStatic\"}} są nieaktualne. Określ właściwość {{\"prototype.name\"}} w nazwie metody zamiast właściwości {{isStatic=false}}.",
|
||||
"c68a93f0a9524fed4ff64372fc90c55f": "Należy podać poprawny adres e-mail",
|
||||
"cd0412f2f33a4a2a316acc834f3f21a6": "należy określić {{id}} lub {{data}}",
|
||||
"d5552322de5605c58b62f47ad26d2716": "Moduł {{`app.boot`}} został usunięty; zamiast niego użyj nowego modułu {{loopback-boot}}",
|
||||
"d6f43b266533b04d442bdb3955622592": "Tworzy nową instancję w elemencie {0} tego modelu.",
|
||||
"da13d3cdf21330557254670dddd8c5c7": "Zlicza elementy {0} w {1}.",
|
||||
"dc568bee32deb0f6eaf63e73b20e8ceb": "Ignorowanie niebędącego obiektem ustawienia \"methods\" elementu \"{0}\".",
|
||||
"e4434de4bb8f5a3cd1d416e4d80d7e0b": "Nieznany identyfikator {{id}} \"{0}\" \"{1}\".",
|
||||
"e92aa25b6b864e3454b65a7c422bd114": "Aktualizacja masowa nie powiodła się, konektor usunął nieoczekiwaną liczbę rekordów: {0}",
|
||||
"ea63d226b6968e328bdf6876010786b5": "Nie można zastosować aktualizacji masowej, ponieważ konektor nie raportuje poprawnie liczby usuniętych rekordów.",
|
||||
"ead044e2b4bce74b4357f8a03fb78ec4": "Nie można wywołać metody {0}.{1}(). Metoda {2} nie została skonfigurowana. Model {{KeyValueModel}} nie został poprawnie przyłączony do źródła danych {{DataSource}}!",
|
||||
"ecb06666ef95e5db27a5ac1d6a17923b": "\t DO:{0}",
|
||||
"f0aed00a3d3d0b97d6594e4b70e0c201": "\t TRANSPORT:{0}",
|
||||
"f0bd73df8714cefb925e3b8da2f4c5f6": "wynik:{0}",
|
||||
"f1d4ac54357cc0932f385d56814ba7e4": "Konflikt",
|
||||
"f66ae3cf379b2fce28575a3282defe1a": "Usuwa element {0} tego modelu.",
|
||||
"f8e26bcca62a47f579562f1cd2c785ff": "Zmodyfikuj aplikację w celu użycia oficjalnego rozwiązania do wstrzykiwania argumentu \"options\" z kontekstu żądania,\npatrz sekcja {0}"
|
||||
}
|
||||
|
|
@ -3,11 +3,13 @@
|
|||
"04bd8af876f001ceaf443aad6a9002f9": "Autenticação requer que modelo {0} seja definido.",
|
||||
"095afbf2f1f0e5be678f5dac5c54e717": "Acesso Negado",
|
||||
"0caffe1d763c8cca6a61814abe33b776": "E-mail é necessário",
|
||||
"0e21aad369dd09e1965c11949303cefd": "Metadados remotos para {0}. {1} {{\"isStatic\"}} não corresponde ao novo estilo baseado em nome do método.",
|
||||
"0da38687fed24275c1547e815914a8e3": "Excluir um item relacionado por ID para {0}.",
|
||||
"0e21aad369dd09e1965c11949303cefd": "Os metadados remotos para {0}.{1} {{\"isStatic\"}} não correspondem ao novo estilo baseado em nome do método. ",
|
||||
"10e01c895dc0b2fecc385f9f462f1ca6": "uma lista de cores está disponível em {{http://localhost:3000/colors}}",
|
||||
"1b2a6076dccbe91a56f1672eb3b8598c": "O corpo de resposta contém propriedades do {{AccessToken}} criado no login.\nDependendo do valor do parâmetro `include`, o corpo poderá conter propriedades adicionais:\n\n - `user` - `U+007BUserU+007D` - Dados do usuário com login efetuado atualmente. {{(`include=user`)}}\n\n",
|
||||
"1d7833c3ca2f05fdad8fad7537531c40": "\t ASSUNTO:{0}",
|
||||
"1e85f822b547a75d7d385048030e4ecb": "Criado: {0}",
|
||||
"22fe62fa8d595b72c62208beddaa2a56": "Atualizar um item relacionado por ID para {0}.",
|
||||
"275f22ab95671f095640ca99194b7635": "\t DE:{0}",
|
||||
"2860bccdf9ef1e350c1a38932ed12173": "{0} foi removido na versão 3.0. Consulte {1} para obter mais detalhes.",
|
||||
"2d3071e3b18681c80a090dc0efbdb349": "não foi possível localizar {0} com ID {1}",
|
||||
|
@ -16,6 +18,7 @@
|
|||
"3438fab56cc7ab92dfd88f0497e523e0": "A propriedade de relações da configuração de `{0}` deve ser um objeto",
|
||||
"3591f1d3e115b46f9f195df5ca548a6a": "Modelo não localizado: modelo `{0}` está estendendo um modelo `{1}` desconhecido.",
|
||||
"35e5252c62d80f8c54a5290d30f4c7d0": "Verifique seu e-mail abrindo este link em um navegador da web:\n\t{0}",
|
||||
"37bcd4b50dfae98734772e39ffb1ea3d": "A senha inserida é muito longa. O comprimento máximo é de {0} ({1} inserido)",
|
||||
"3aae63bb7e8e046641767571c1591441": "login com falha pois o e-mail não foi verificado",
|
||||
"3aecb24fa8bdd3f79d168761ca8a6729": "Fase {0} do {{middleware}} desconhecida",
|
||||
"3ca45aa6f705c46a4c598a900716f086": "{0} está usando a configuração do modelo {1} que não está mais disponível.",
|
||||
|
@ -27,41 +30,54 @@
|
|||
"44a6c8b1ded4ed653d19ddeaaf89a606": "E-mail não encontrado",
|
||||
"4a4f04a4e480fc5d4ee73b84d9a4b904": "Enviando E-mail:",
|
||||
"4b494de07f524703ac0879addbd64b13": "E-mail não foi verificado",
|
||||
"528325f3cbf1b0ab9a08447515daac9a": "Atualizar {0} deste modelo.",
|
||||
"543d19bad5e47ee1e9eb8af688e857b4": "Chave estrangeira para {0}.",
|
||||
"57b87ae0e65f6ab7a2e3e6cbdfca49a4": "Não é possível criar origem de dados {0}: {1}",
|
||||
"5858e63efaa0e4ad86b61c0459ea32fa": "Deve-se conectar o Modelo de {{Email}} em um conector de {{Mail}}",
|
||||
"598ff0255ffd1d1b71e8de55dbe2c034": "Verifique a existência da relação de {0} com um item por ID.",
|
||||
"5a36cc6ba0cc27c754f6c5ed6015ea3c": "Remova a relação de {0} com um item por ID.",
|
||||
"5e81ad3847a290dc650b47618b9cbc7e": "falha de login",
|
||||
"5fa3afb425819ebde958043e598cb664": "não foi possível localizar um modelo com {{id}} {0}",
|
||||
"61e5deebaf44d68f4e6a508f30cc31a3": "Relação `{0}` não existe para o modelo `{1}`",
|
||||
"62e8b0a733417978bab22c8dacf5d7e6": "Não é possível aplicar atualizações em massa, o conector não relata o número de registros de atualização corretamente.",
|
||||
"63a091ced88001ab6acb58f61ec041c5": "\t TEXTO:{0}",
|
||||
"651f0b3cbba001635152ec3d3d954d0a": "Localize um item relacionado por ID para {0}.",
|
||||
"6bc376432cd9972cf991aad3de371e78": "Dados ausentes para a mudança: {0}",
|
||||
"705c2d456a3e204c4af56e671ec3225c": "Não foi possível localizar o {{accessToken}}",
|
||||
"734a7bebb65e10899935126ba63dd51f": "A propriedade de opções da configuração de `{0}` deve ser um objeto",
|
||||
"779467f467862836e19f494a37d6ab77": "A propriedade acls da configuração de `{0}` deve ser uma matriz de objetos",
|
||||
"7bc7b301ad9c4fc873029d57fb9740fe": "{0} consultas de {1}.",
|
||||
"7c837b88fd0e509bd3fc722d7ddf0711": "Chave estrangeira para {0}",
|
||||
"7d5e7ed0efaedf3f55f380caae0df8b8": "Meu primeiro aplicativo móvel",
|
||||
"7e0fca41d098607e1c9aa353c67e0fa1": "Token de Acesso Inválido",
|
||||
"7e287fc885d9fdcf42da3a12f38572c1": "Autorização Necessária",
|
||||
"7ea04ea91aac3cb7ce0ddd96b7ff1fa4": "{{accessToken}} é necessário para efetuar logout",
|
||||
"80a32e80cbed65eba2103201a7c94710": "Modelo não localizado: {0}",
|
||||
"830cb6c862f8f364e9064cea0026f701": "Busca relação {0} de hasOne.",
|
||||
"83cbdc2560ba9f09155ccfc63e08f1a1": "A propriedade `{0}` não pode ser reconfigurada para `{1}`",
|
||||
"855eb8db89b4921c42072832d33d2dc2": "Senha inválida.",
|
||||
"855ecd4a64885ba272d782435f72a4d4": "ID \"{1}\" de \"{0}\" desconhecido.",
|
||||
"860d1a0b8bd340411fb32baa72867989": "O transporte não suporta redirecionamentos de HTTP.",
|
||||
"86254879d01a60826a851066987703f2": "Inclua um item relacionado por ID para {0}.",
|
||||
"895b1f941d026870b3cc8e6af087c197": "{{username}} ou {{email}} é necessário",
|
||||
"8a17c5ef611e2e7535792316e66b8fca": "Senha muito longa: {0}",
|
||||
"8ae418c605b6a45f2651be9b1677c180": "Método remoto inválido: `{0}`",
|
||||
"8bab6720ecc58ec6412358c858a53484": "Atualização em massa falhou, o conector modificou um número inesperado de registros: {0}",
|
||||
"8ecab7f534de38360bd1b1c88e880123": "Modelos filhos de `{0}` não herdarão métodos remotos recém-definidos {1}.",
|
||||
"93ba9a1d03da3b7696332d3f155c5bb7": "\t HTML:{0}",
|
||||
"97795efe0c3eb7f35ce8cf8cfe70682b": "A configuração de `{0}` não possui a propriedade {{`dataSource`}}.\nUse `null` ou `false` para marcar modelos não conectados a nenhuma origem de dados.",
|
||||
"9e3cbc1d5a9347cdcf6d1b4a6dbb55b7": "Busca relação {0} de belongsTo.",
|
||||
"a50d10fc6e0959b220e085454c40381e": "Usuário não localizado: {0}",
|
||||
"b6f740aeb6f2eb9bee9cb049dbfe6a28": "\"{0}\" {{key}} \"{1}\" desconhecido.",
|
||||
"ba96498b10c179f9cd75f75c8def4f70": "{{realm}} é obrigatório",
|
||||
"c0057a569ff9d3b509bac61a4b2f605d": "Exclui todos os {0} deste modelo.",
|
||||
"c2b5d51f007178170ca3952d59640ca4": "Não é possível retificar mudanças de {0}:\n{1}",
|
||||
"c4ee6d177c974532c3552d2f98eb72ea": "{0} middleware foi removido na versão 3.0. Consulte {1} para obter mais detalhes.",
|
||||
"c61a5a02ba3801a892308f70f5d55a14": "Metadados {{\"isStatic\"}} remotos estão descontinuados. Especifique {{\"prototype.name\"}} no nome do método em vez de para {{isStatic=false}}.",
|
||||
"c68a93f0a9524fed4ff64372fc90c55f": "Deve-se fornecer um e-mail válido",
|
||||
"cd0412f2f33a4a2a316acc834f3f21a6": "deve-se especificar um {{id}} ou {{data}}",
|
||||
"d5552322de5605c58b62f47ad26d2716": "{{`app.boot`}} foi removido, use o novo módulo {{loopback-boot}} no lugar",
|
||||
"d6f43b266533b04d442bdb3955622592": "Cria uma nova instância no {0} deste modelo.",
|
||||
"da13d3cdf21330557254670dddd8c5c7": "{0} contagens de {1}.",
|
||||
"dc568bee32deb0f6eaf63e73b20e8ceb": "Ignorando configuração de \"methods\" de não objeto de \"{0}\".",
|
||||
"e4434de4bb8f5a3cd1d416e4d80d7e0b": "\"{0}\" {{id}} \"{1}\" desconhecido.",
|
||||
"e92aa25b6b864e3454b65a7c422bd114": "Atualização em massa falhou, o conector excluiu um número inesperado de registros: {0}",
|
||||
|
@ -71,7 +87,7 @@
|
|||
"f0aed00a3d3d0b97d6594e4b70e0c201": "\t TRANSPORTE:{0}",
|
||||
"f0bd73df8714cefb925e3b8da2f4c5f6": "resultado:{0}",
|
||||
"f1d4ac54357cc0932f385d56814ba7e4": "Conflito",
|
||||
"f58cdc481540cd1f69a4aa4da2e37981": "Senha inválida: {0}",
|
||||
"f66ae3cf379b2fce28575a3282defe1a": "Exclui {0} deste modelo.",
|
||||
"f8e26bcca62a47f579562f1cd2c785ff": "Retrabalhe seu aplicativo para usar a solução oficial para injetar o argumento \"options\" do contexto da solicitação,\nconsulte {0}"
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,93 @@
|
|||
{
|
||||
"03f79fa268fe199de2ce4345515431c1": "Не удалось найти записи изменений для {0} с ИД {1}",
|
||||
"04bd8af876f001ceaf443aad6a9002f9": "Для идентификации необходимо определить модель {0}.",
|
||||
"095afbf2f1f0e5be678f5dac5c54e717": "Доступ запрещен",
|
||||
"0caffe1d763c8cca6a61814abe33b776": "Необходимо указать адрес электронной почты",
|
||||
"0da38687fed24275c1547e815914a8e3": "Удалить связанный элемент по ИД для {0}.",
|
||||
"0e21aad369dd09e1965c11949303cefd": "Метаданные удаленного соединения для {0}.{1} {{\"isStatic\"}} не соответствуют новому стилю на основе имени метода.",
|
||||
"10e01c895dc0b2fecc385f9f462f1ca6": "Список цветов доступен по адресу {{http://localhost:3000/colors}}",
|
||||
"1b2a6076dccbe91a56f1672eb3b8598c": "Тело ответа содержит свойства маркера {{AccessToken}}, созданного при входе в систему.\nВ зависимости от значения параметра `include`, тело может содержать дополнительные свойства:\n\n - `user` - `U+007BUserU+007D` - данные пользователя, вошедшего в систему. {{(`include=user`)}}\n\n",
|
||||
"1d7833c3ca2f05fdad8fad7537531c40": "\t ТЕМА:{0}",
|
||||
"1e85f822b547a75d7d385048030e4ecb": "Кем создано: {0}",
|
||||
"22fe62fa8d595b72c62208beddaa2a56": "Изменить связанный элемент по ИД для {0}.",
|
||||
"275f22ab95671f095640ca99194b7635": "\t От кого:{0}",
|
||||
"2860bccdf9ef1e350c1a38932ed12173": "{0} удален в версии 3.0. Дополнительные сведения приведены в {1}.",
|
||||
"2d3071e3b18681c80a090dc0efbdb349": "не удалось найти {0} с ИД {1}",
|
||||
"316e5b82c203cf3de31a449ee07d0650": "Ожидался тип boolean, получен тип {0}",
|
||||
"320c482401afa1207c04343ab162e803": "Недопустимый тип субъекта: {0}",
|
||||
"3438fab56cc7ab92dfd88f0497e523e0": "Свойство relations конфигурации `{0}` должно быть объектом",
|
||||
"3591f1d3e115b46f9f195df5ca548a6a": "Модель не найдена: модель `{0}` расширяет неизвестную модель `{1}`.",
|
||||
"35e5252c62d80f8c54a5290d30f4c7d0": "Проверьте адрес электронной почты. Для этого откройте ссылку в веб-браузере:\n\t{0}",
|
||||
"37bcd4b50dfae98734772e39ffb1ea3d": "Введен слишком длинный пароль. Максимальная длина составляет {0} символов (введено {1} символов)",
|
||||
"3aae63bb7e8e046641767571c1591441": "Вход в систему не выполнен, так как не проверен адрес электронной почты",
|
||||
"3aecb24fa8bdd3f79d168761ca8a6729": "Неизвестный этап {{middleware}} {0}",
|
||||
"3ca45aa6f705c46a4c598a900716f086": "{0} использует параметр модели {1}, который больше не доступен.",
|
||||
"3caaa84fc103d6d5612173ae6d43b245": "Недопустимый маркер: {0}",
|
||||
"3d617953470be16d0c2b32f0bcfbb5ee": "Спасибо за регистрацию",
|
||||
"3d63008ccfb2af1db2142e8cc2716ace": "Предупреждение: не указан транспортный протокол для отправки электронной почты. Настройте транспортный протокол для отправки сообщений электронной почты.",
|
||||
"4203ab415ec66a78d3164345439ba76e": "Не удалось вызвать {0}. {1} (). Метод {2} не настроен. Модель {{PersistedModel}} неправильно подключена к источнику данных {{DataSource}}!",
|
||||
"42a36bac5cf03c4418d664500c81047a": "Опция {{DataSource}} {{\"defaultForType\"}} больше не поддерживается",
|
||||
"44a6c8b1ded4ed653d19ddeaaf89a606": "Не найден адрес электронной почты",
|
||||
"4a4f04a4e480fc5d4ee73b84d9a4b904": "Отправка почты:",
|
||||
"4b494de07f524703ac0879addbd64b13": "Не проверен адрес электронной почты",
|
||||
"528325f3cbf1b0ab9a08447515daac9a": "Обновить {0} этой модели.",
|
||||
"543d19bad5e47ee1e9eb8af688e857b4": "Внешний ключ для {0}.",
|
||||
"57b87ae0e65f6ab7a2e3e6cbdfca49a4": "Не удалось создать источник данных {0}: {1}",
|
||||
"5858e63efaa0e4ad86b61c0459ea32fa": "Необходимо подключить модель {{Email}} к коннектору {{Mail}}",
|
||||
"598ff0255ffd1d1b71e8de55dbe2c034": "Проверьте существование связи {0} с элементом по ИД.",
|
||||
"5a36cc6ba0cc27c754f6c5ed6015ea3c": "Удалить связь {0} с элементом по ИД.",
|
||||
"5e81ad3847a290dc650b47618b9cbc7e": "Вход в систему не выполнен",
|
||||
"5fa3afb425819ebde958043e598cb664": "Не удалось найти модель с {{id}} {0}",
|
||||
"61e5deebaf44d68f4e6a508f30cc31a3": "Связь `{0}` не существует для модели `{1}`",
|
||||
"62e8b0a733417978bab22c8dacf5d7e6": "Не удалось применить обновления большого объема данных, коннектор неправильно сообщает о числе обновленных записей.",
|
||||
"63a091ced88001ab6acb58f61ec041c5": "\t ТЕКСТ: {0}",
|
||||
"651f0b3cbba001635152ec3d3d954d0a": "Найти связанный элемент по ИД для {0}.",
|
||||
"6bc376432cd9972cf991aad3de371e78": "Отсутствуют данные для изменения: {0}",
|
||||
"705c2d456a3e204c4af56e671ec3225c": "Не удалось найти {{accessToken}}",
|
||||
"734a7bebb65e10899935126ba63dd51f": "Свойство options конфигурации `{0}` должно быть объектом",
|
||||
"779467f467862836e19f494a37d6ab77": "Свойство acls конфигурации `{0}` должно быть массивом объектов",
|
||||
"7bc7b301ad9c4fc873029d57fb9740fe": "{0} запросов из {1}.",
|
||||
"7c837b88fd0e509bd3fc722d7ddf0711": "Внешний ключ для {0}",
|
||||
"7d5e7ed0efaedf3f55f380caae0df8b8": "Мое первое мобильное приложение",
|
||||
"7e0fca41d098607e1c9aa353c67e0fa1": "Недопустимый маркер доступа",
|
||||
"7e287fc885d9fdcf42da3a12f38572c1": "Требуется авторизация",
|
||||
"7ea04ea91aac3cb7ce0ddd96b7ff1fa4": "{{accessToken}} требуется для завершения сеанса",
|
||||
"80a32e80cbed65eba2103201a7c94710": "Модель не найдена: {0}",
|
||||
"830cb6c862f8f364e9064cea0026f701": "Получает связь hasOne {0}.",
|
||||
"83cbdc2560ba9f09155ccfc63e08f1a1": "Не удается повторно выполнить конфигурацию свойства `{0}` для `{1}`",
|
||||
"855eb8db89b4921c42072832d33d2dc2": "Недопустимый пароль.",
|
||||
"855ecd4a64885ba272d782435f72a4d4": "Неизвестный ИД \"{0}\" \"{1}\".",
|
||||
"860d1a0b8bd340411fb32baa72867989": "Транспортный протокол не поддерживает перенаправление HTTP.",
|
||||
"86254879d01a60826a851066987703f2": "Добавить связанный элемент по ИД для {0}.",
|
||||
"895b1f941d026870b3cc8e6af087c197": "Необходимо указать {{username}} или {{email}}",
|
||||
"8ae418c605b6a45f2651be9b1677c180": "Недопустимый удаленный метод: `{0}`",
|
||||
"8bab6720ecc58ec6412358c858a53484": "Обновление большого объема данных не выполнено, коннектор изменил непредвиденное число записей: {0}",
|
||||
"8ecab7f534de38360bd1b1c88e880123": "Дочерние модели ` {0} ' не будут наследовать только что определенные удаленные методы {1}.",
|
||||
"93ba9a1d03da3b7696332d3f155c5bb7": "\t HTML:{0}",
|
||||
"97795efe0c3eb7f35ce8cf8cfe70682b": "В конфигурации `{0}` отсутствует свойство {{`dataSource`}}.\nДля пометки моделей, которые не подключены в источникам данных, используйте значение `null` или `false`.",
|
||||
"9e3cbc1d5a9347cdcf6d1b4a6dbb55b7": "Получает связь belongsTo {0}.",
|
||||
"a50d10fc6e0959b220e085454c40381e": "Пользователь не найден: {0}",
|
||||
"b6f740aeb6f2eb9bee9cb049dbfe6a28": "Неизвестный {{key}} \"{0}\" \"{1}\".",
|
||||
"ba96498b10c179f9cd75f75c8def4f70": "Необходимо указать {{realm}}",
|
||||
"c0057a569ff9d3b509bac61a4b2f605d": "Удалить все {0} этой модели.",
|
||||
"c2b5d51f007178170ca3952d59640ca4": "Не удалось исправить изменения {0}:\n{1}",
|
||||
"c4ee6d177c974532c3552d2f98eb72ea": "Промежуточное ПО {0} удалено в версии 3.0. Дополнительные сведения приведены в {1}.",
|
||||
"c61a5a02ba3801a892308f70f5d55a14": "Метаданные удаленного соединения {{\"isStatic\"}} устарели. В имени метода вместо {{isStatic=false}} укажите {{\"prototype.name\"}}.",
|
||||
"c68a93f0a9524fed4ff64372fc90c55f": "Необходимо указать допустимый адрес электронной почты",
|
||||
"cd0412f2f33a4a2a316acc834f3f21a6": "Необходимо указать {{id}} или {{data}}",
|
||||
"d5552322de5605c58b62f47ad26d2716": "Модуль {{`app.boot`}} был удален, используйте вместо него новый модуль {{loopback-boot}}",
|
||||
"d6f43b266533b04d442bdb3955622592": "Создает новый экземпляр в {0} этой модели.",
|
||||
"da13d3cdf21330557254670dddd8c5c7": "Считает {0} из {1}.",
|
||||
"dc568bee32deb0f6eaf63e73b20e8ceb": "Не относящийся к объекту параметр \"methods\" для \"{0}\" игнорируется.",
|
||||
"e4434de4bb8f5a3cd1d416e4d80d7e0b": "Неизвестный {{id}} \"{0}\" \"{1}\".",
|
||||
"e92aa25b6b864e3454b65a7c422bd114": "Обновление большого объема данных не выполнено, коннектор удалил непредвиденное число записей: {0}",
|
||||
"ea63d226b6968e328bdf6876010786b5": "Не удалось применить обновления большого объема данных, коннектор неправильно сообщает о числе удаленных записей.",
|
||||
"ead044e2b4bce74b4357f8a03fb78ec4": "Не удалось вызвать {0}.{1}(). Метод {2} не настроен. Модель {{KeyValueModel}} неправильно подключена к источнику данных {{DataSource}}!",
|
||||
"ecb06666ef95e5db27a5ac1d6a17923b": "\t Кому:{0}",
|
||||
"f0aed00a3d3d0b97d6594e4b70e0c201": "\t Транспортный протокол:{0}",
|
||||
"f0bd73df8714cefb925e3b8da2f4c5f6": "результат:{0}",
|
||||
"f1d4ac54357cc0932f385d56814ba7e4": "Конфликт",
|
||||
"f66ae3cf379b2fce28575a3282defe1a": "Удалить {0} этой модели.",
|
||||
"f8e26bcca62a47f579562f1cd2c785ff": "Доработайте приложение с целью использования официального решения для добавления аргумента \"options\" из контекста запроса,\nсм. {0}"
|
||||
}
|
||||
|
|
@ -3,11 +3,13 @@
|
|||
"04bd8af876f001ceaf443aad6a9002f9": "Kimlik doğrulaması {0} modelinin tanımlanmasını gerektiriyor.",
|
||||
"095afbf2f1f0e5be678f5dac5c54e717": "Erişim Verilmedi",
|
||||
"0caffe1d763c8cca6a61814abe33b776": "E-posta zorunludur",
|
||||
"0e21aad369dd09e1965c11949303cefd": "{0} için meta veriler kaldırılıyor. {1} {{\"isStatic\"}} yeni yöntem adına dayalı stille eşleşmiyor.",
|
||||
"0da38687fed24275c1547e815914a8e3": "{0} için ilgili bir öğeyi tanıtıcı temelinde siler.",
|
||||
"0e21aad369dd09e1965c11949303cefd": "{0}.{1} {{\"isStatic\"}} ile ilgili uzaktan iletişim meta verisi, yöntem adına dayalı yeni stille eşleşmiyor.",
|
||||
"10e01c895dc0b2fecc385f9f462f1ca6": "renklerin listesine şu adresle erişebilirsiniz: {{http://localhost:3000/colors}}",
|
||||
"1b2a6076dccbe91a56f1672eb3b8598c": "Yanıt gövdesi, oturum açma sırasında yaratılan {{AccessToken}} belirtecine ilişkin özellikleri içerir.\n`include` parametresinin değerine bağlı olarak, gövde ek özellikler içerebilir:\n\n - `user` - `U+007BUserU+007D` - Oturum açmış olan kullanıcıya ilişkin veriler. {{(`include=user`)}}\n\n",
|
||||
"1d7833c3ca2f05fdad8fad7537531c40": "\t KONU:{0}",
|
||||
"1e85f822b547a75d7d385048030e4ecb": "Yaratıldığı tarih: {0}",
|
||||
"22fe62fa8d595b72c62208beddaa2a56": "{0} için ilgili bir öğeyi tanıtıcı temelinde günceller.",
|
||||
"275f22ab95671f095640ca99194b7635": "\t KİMDEN:{0}",
|
||||
"2860bccdf9ef1e350c1a38932ed12173": "{0}, 3.0 sürümünde kaldırıldı. Daha fazla ayrıntı için bkz. {1}.",
|
||||
"2d3071e3b18681c80a090dc0efbdb349": "{1} tanıtıcılı {0} bulunamadı",
|
||||
|
@ -16,62 +18,76 @@
|
|||
"3438fab56cc7ab92dfd88f0497e523e0": "`{0}` yapılandırmasının ilişkiler (relations) özelliği bir nesne olmalıdır",
|
||||
"3591f1d3e115b46f9f195df5ca548a6a": "Model bulunamadı: `{0}` modeli, bilinmeyen `{1}` modelini genişletiyor.",
|
||||
"35e5252c62d80f8c54a5290d30f4c7d0": "Lütfen bu bağlantıyı bir web tarayıcısında açarak e-postanızı doğrulayın:\n\t{0}",
|
||||
"37bcd4b50dfae98734772e39ffb1ea3d": "Girilen parola çok uzundu. Uzunluk üst sınırı: {0} (girilen: {1})",
|
||||
"3aae63bb7e8e046641767571c1591441": "e-posta doğrulanmadığından oturum açma başarısız oldu",
|
||||
"3aecb24fa8bdd3f79d168761ca8a6729": "Bilinmeyen {{middleware}} aşaması {0}",
|
||||
"3ca45aa6f705c46a4c598a900716f086": "{0}, artık kullanılabilir olmayan {1} model ayarını kullanıyor.",
|
||||
"3caaa84fc103d6d5612173ae6d43b245": "Geçersiz belirteç: {0}",
|
||||
"3d617953470be16d0c2b32f0bcfbb5ee": "Kaydolduğunuz için teşekkürler",
|
||||
"3d63008ccfb2af1db2142e8cc2716ace": "Uyarı: E-posta göndermek için e-posta aktarımı belirtilmedi. Posta iletileri göndermek için aktarım ayarlayın.",
|
||||
"4203ab415ec66a78d3164345439ba76e": "Çağrılamıyor: {0}.{1}(). {2} yöntemi ayarlanmamış. {{PersistedModel}}, bir veri kaynağına ({{DataSource}}) doğru olarak eklenmedi!",
|
||||
"4203ab415ec66a78d3164345439ba76e": "{0}.{1}() çağrılamıyor. {2} yöntemi ayarlanmamış. {{PersistedModel}}, bir veri kaynağına ({{DataSource}}) doğru olarak eklenmedi!",
|
||||
"42a36bac5cf03c4418d664500c81047a": "{{DataSource}} seçeneği {{\"defaultForType\"}} artık desteklenmiyor.",
|
||||
"44a6c8b1ded4ed653d19ddeaaf89a606": "E-posta bulunamadı",
|
||||
"4a4f04a4e480fc5d4ee73b84d9a4b904": "Posta Gönderiliyor:",
|
||||
"4b494de07f524703ac0879addbd64b13": "E-posta doğrulanmadı",
|
||||
"528325f3cbf1b0ab9a08447515daac9a": "Bu modele ilişkin {0} güncellemesi.",
|
||||
"543d19bad5e47ee1e9eb8af688e857b4": "{0} için dış anahtar.",
|
||||
"57b87ae0e65f6ab7a2e3e6cbdfca49a4": "Veri kaynağı {0} yaratılamıyor: {1}",
|
||||
"5858e63efaa0e4ad86b61c0459ea32fa": "{{Email}} modelini bir {{Mail}} bağlayıcısına bağlamalısınız",
|
||||
"598ff0255ffd1d1b71e8de55dbe2c034": "Bir öğeye yönelik {0} ilişkisinin var olup olmadığını tanıtıcı temelinde denetler.",
|
||||
"5a36cc6ba0cc27c754f6c5ed6015ea3c": "Bir öğeye yönelik {0} ilişkisini kaldırır.",
|
||||
"5e81ad3847a290dc650b47618b9cbc7e": "oturum açma başarısız oldu",
|
||||
"5fa3afb425819ebde958043e598cb664": "{{id}} {0} tanıtıcılı bir model bulunamadı",
|
||||
"61e5deebaf44d68f4e6a508f30cc31a3": "`{1}` modeli için `{0}` ilişkisi yok",
|
||||
"62e8b0a733417978bab22c8dacf5d7e6": "Toplu güncelleme uygulanamaz; bağlayıcı, güncellenen kayıtların sayısını doğru olarak bildirmiyor.",
|
||||
"63a091ced88001ab6acb58f61ec041c5": "\t METİN:{0}",
|
||||
"651f0b3cbba001635152ec3d3d954d0a": "{0} için ilgili bir öğeyi tanıtıcı temelinde bulur.",
|
||||
"6bc376432cd9972cf991aad3de371e78": "Değişiklik için veri eksik: {0}",
|
||||
"705c2d456a3e204c4af56e671ec3225c": "{{accessToken}} bulunamadı",
|
||||
"734a7bebb65e10899935126ba63dd51f": "`{0}` yapılandırmasının seçenekler (options) özelliği bir nesne olmalıdır.",
|
||||
"779467f467862836e19f494a37d6ab77": "`{0}` yapılandırmasının erişim denetim listeleri (acls) özelliği bir nesne dizisi olmalıdır.",
|
||||
"7bc7b301ad9c4fc873029d57fb9740fe": "{1} ile ilişkili {0} öğesini sorgular.",
|
||||
"7c837b88fd0e509bd3fc722d7ddf0711": "{0} için dış anahtar",
|
||||
"7d5e7ed0efaedf3f55f380caae0df8b8": "İlk mobil uygulamam",
|
||||
"7e0fca41d098607e1c9aa353c67e0fa1": "Geçersiz Erişim Belirteci",
|
||||
"7e287fc885d9fdcf42da3a12f38572c1": "Yetkilendirme Gerekli",
|
||||
"7ea04ea91aac3cb7ce0ddd96b7ff1fa4": "Oturumu kapatmak için {{accessToken}} zorunludur",
|
||||
"80a32e80cbed65eba2103201a7c94710": "Model bulunamadı: {0}",
|
||||
"830cb6c862f8f364e9064cea0026f701": "{0} hasOne ilişkisini alır.",
|
||||
"83cbdc2560ba9f09155ccfc63e08f1a1": "`{0}` özelliği `{1}` için yeniden yapılandırılamıyor",
|
||||
"855eb8db89b4921c42072832d33d2dc2": "Geçersiz parola.",
|
||||
"855ecd4a64885ba272d782435f72a4d4": "Bilinmeyen \"{0}\" tanıtıcısı \"{1}\".",
|
||||
"860d1a0b8bd340411fb32baa72867989": "Aktarım HTTP yeniden yönlendirmelerini desteklemiyor.",
|
||||
"86254879d01a60826a851066987703f2": "{0} için ilgili bir öğeyi tanıtıcı temelinde ekler.",
|
||||
"895b1f941d026870b3cc8e6af087c197": "{{username}} ya da {{email}} zorunludur",
|
||||
"8a17c5ef611e2e7535792316e66b8fca": "Parola çok uzun: {0}",
|
||||
"8ae418c605b6a45f2651be9b1677c180": "Uzak yöntem geçersiz: `{0}`",
|
||||
"8bab6720ecc58ec6412358c858a53484": "Toplu güncelleme başarısız oldu, bağlayıcı beklenmeyen sayıda kaydı değiştirdi: {0}",
|
||||
"8ecab7f534de38360bd1b1c88e880123": "`{0}` alt modelleri, yeni tanımlanan uzak yöntemleri ({1}) devralmayacaktır.",
|
||||
"93ba9a1d03da3b7696332d3f155c5bb7": "\t HTML:{0}",
|
||||
"97795efe0c3eb7f35ce8cf8cfe70682b": "`{0}` yapılandırmasında {{`dataSource`}} özelliği eksik.\nHiçbir veri kaynağına eklenmemiş modelleri işaretlemek için `null` ya da `false` kullanın.",
|
||||
"9e3cbc1d5a9347cdcf6d1b4a6dbb55b7": "{0} belongsTo ilişkisini alır.",
|
||||
"a50d10fc6e0959b220e085454c40381e": "Kullanıcı bulunamadı: {0}",
|
||||
"b6f740aeb6f2eb9bee9cb049dbfe6a28": "Bilinmeyen \"{0}\" {{key}} \"{1}\".",
|
||||
"ba96498b10c179f9cd75f75c8def4f70": "{{realm}} zorunludur",
|
||||
"c0057a569ff9d3b509bac61a4b2f605d": "Bu modele ilişkin tüm {0} öğelerini siler.",
|
||||
"c2b5d51f007178170ca3952d59640ca4": "{0} değişiklik düzeltilemiyor:\n{1}",
|
||||
"c4ee6d177c974532c3552d2f98eb72ea": "{0} ara katman yazılımı, 3.0 sürümünde kaldırıldı. Daha fazla ayrıntı için bkz. {1}.",
|
||||
"c61a5a02ba3801a892308f70f5d55a14": "{{\"isStatic\"}} uzaktan iletişim meta verisi kullanım dışı bırakıldı. Lütfen, yöntem adında {{isStatic=false}} yerine {{\"prototype.name\"}} belirtin.",
|
||||
"c68a93f0a9524fed4ff64372fc90c55f": "Geçerli bir e-posta belirtilmeli",
|
||||
"cd0412f2f33a4a2a316acc834f3f21a6": "bir {{id}} ya da {{data}} belirtmelidir",
|
||||
"d5552322de5605c58b62f47ad26d2716": "{{`app.boot`}} kaldırıldı, onun yerine yeni {{loopback-boot}} modülünü kullanın",
|
||||
"d6f43b266533b04d442bdb3955622592": "Bu modele ilişkin {0} içinde yeni eşgörünüm yaratır.",
|
||||
"da13d3cdf21330557254670dddd8c5c7": "{1} ile ilişkili {0} öğesini sayar.",
|
||||
"dc568bee32deb0f6eaf63e73b20e8ceb": "\"{0}\" öğesinin nesne olmayan \"methods\" atarı yoksayılıyor.",
|
||||
"e4434de4bb8f5a3cd1d416e4d80d7e0b": "Bilinmeyen \"{0}\" {{id}} \"{1}\".",
|
||||
"e92aa25b6b864e3454b65a7c422bd114": "Toplu güncelleme başarısız oldu, bağlayıcı beklenmeyen sayıda kaydı sildi: {0}",
|
||||
"ea63d226b6968e328bdf6876010786b5": "Toplu güncelleme uygulanamaz; bağlayıcı, silinen kayıtların sayısını doğru olarak bildirmiyor.",
|
||||
"ead044e2b4bce74b4357f8a03fb78ec4": "Çağrılamıyor: {0}.{1}(). {2} yöntemi ayarlanmamış. {{KeyValueModel}}, bir veri kaynağına ({{DataSource}}) doğru olarak eklenmedi!",
|
||||
"ead044e2b4bce74b4357f8a03fb78ec4": "{0}.{1}() çağrılamıyor. {2} yöntemi ayarlanmamış. {{KeyValueModel}}, bir veri kaynağına ({{DataSource}}) doğru olarak eklenmedi!",
|
||||
"ecb06666ef95e5db27a5ac1d6a17923b": "\t KİME:{0}",
|
||||
"f0aed00a3d3d0b97d6594e4b70e0c201": "\t AKTARIM:{0}",
|
||||
"f0bd73df8714cefb925e3b8da2f4c5f6": "sonuç:{0}",
|
||||
"f1d4ac54357cc0932f385d56814ba7e4": "Çakışma",
|
||||
"f58cdc481540cd1f69a4aa4da2e37981": "Geçersiz parola: {0}",
|
||||
"f66ae3cf379b2fce28575a3282defe1a": "Bu modele ilişkin {0} öğesini siler.",
|
||||
"f8e26bcca62a47f579562f1cd2c785ff": "Lütfen istek bağlamından \"seçenekler\" bağımsız değişkenini eklemek amacıyla resmi çözümü kullanmak için uygulamanız üzerinde yeniden çalışın,\nbkz. {0}"
|
||||
}
|
||||
|
||||
|
|
|
@ -3,11 +3,13 @@
|
|||
"04bd8af876f001ceaf443aad6a9002f9": "认证需要定义模型 {0}。",
|
||||
"095afbf2f1f0e5be678f5dac5c54e717": "拒绝访问",
|
||||
"0caffe1d763c8cca6a61814abe33b776": "电子邮件是必需的",
|
||||
"0da38687fed24275c1547e815914a8e3": "按标识删除 {0} 的相关项。",
|
||||
"0e21aad369dd09e1965c11949303cefd": "{0}.{1} {{\"isStatic\"}} 的远程处理元数据不匹配新的基于方法名称的样式。",
|
||||
"10e01c895dc0b2fecc385f9f462f1ca6": "颜色列表位于:{{http://localhost:3000/colors}}",
|
||||
"1b2a6076dccbe91a56f1672eb3b8598c": "响应主体包含在登录时创建的 {{AccessToken}} 的属性。\n根据“include”参数的值,主体可包含其他属性:\n\n - `user` - `U+007BUserU+007D` - 当前已登录用户的数据。 {{(`include=user`)}}\n\n",
|
||||
"1d7833c3ca2f05fdad8fad7537531c40": "\t主题:{0}",
|
||||
"1e85f822b547a75d7d385048030e4ecb": "创建时间:{0}",
|
||||
"22fe62fa8d595b72c62208beddaa2a56": "按标识更新 {0} 的相关项。",
|
||||
"275f22ab95671f095640ca99194b7635": "\t发件人:{0}",
|
||||
"2860bccdf9ef1e350c1a38932ed12173": "V3.0 中移除了 {0}。请参阅 {1} 以获取更多详细信息。",
|
||||
"2d3071e3b18681c80a090dc0efbdb349": "无法找到标识为 {1} 的 {0}",
|
||||
|
@ -16,6 +18,7 @@
|
|||
"3438fab56cc7ab92dfd88f0497e523e0": "“{0}”配置的关系属性必须是对象。",
|
||||
"3591f1d3e115b46f9f195df5ca548a6a": "找不到模型:模型“{0}”正在扩展未知的模型“{1}”。",
|
||||
"35e5252c62d80f8c54a5290d30f4c7d0": "请通过在 Web 浏览器中打开此链接来验证您的电子邮件:\n\t{0}",
|
||||
"37bcd4b50dfae98734772e39ffb1ea3d": "输入的密码过长。最大长度为 {0}(输入的长度为 {1})",
|
||||
"3aae63bb7e8e046641767571c1591441": "因为尚未验证电子邮件,登录失败",
|
||||
"3aecb24fa8bdd3f79d168761ca8a6729": "未知的 {{middleware}} 阶段 {0}",
|
||||
"3ca45aa6f705c46a4c598a900716f086": "{0} 正在使用目前不再可用的模型设置 {1}。",
|
||||
|
@ -27,41 +30,54 @@
|
|||
"44a6c8b1ded4ed653d19ddeaaf89a606": "找不到电子邮件",
|
||||
"4a4f04a4e480fc5d4ee73b84d9a4b904": "正在发送电子邮件:",
|
||||
"4b494de07f524703ac0879addbd64b13": "尚未验证电子邮件",
|
||||
"528325f3cbf1b0ab9a08447515daac9a": "更新此模型的 {0}。",
|
||||
"543d19bad5e47ee1e9eb8af688e857b4": "{0} 的外键。",
|
||||
"57b87ae0e65f6ab7a2e3e6cbdfca49a4": "无法创建数据源 {0}:{1}",
|
||||
"5858e63efaa0e4ad86b61c0459ea32fa": "您必须将 {{Email}} 模型连接到 {{Mail}} 连接器",
|
||||
"598ff0255ffd1d1b71e8de55dbe2c034": "按标识检查项的 {0} 关系是否存在。",
|
||||
"5a36cc6ba0cc27c754f6c5ed6015ea3c": "按标识除去项的 {0} 关系。",
|
||||
"5e81ad3847a290dc650b47618b9cbc7e": "登录失败",
|
||||
"5fa3afb425819ebde958043e598cb664": "找不到具有 {{id}} {0} 的模型",
|
||||
"61e5deebaf44d68f4e6a508f30cc31a3": "对于模型“{1}”,关系“{0}”不存在",
|
||||
"62e8b0a733417978bab22c8dacf5d7e6": "无法应用批量更新,连接器未正确报告更新的记录数。",
|
||||
"63a091ced88001ab6acb58f61ec041c5": "\t 文本:{0}",
|
||||
"651f0b3cbba001635152ec3d3d954d0a": "按标识查找 {0} 的相关项。",
|
||||
"6bc376432cd9972cf991aad3de371e78": "缺少更改的数据:{0}",
|
||||
"705c2d456a3e204c4af56e671ec3225c": "无法找到 {{accessToken}}",
|
||||
"734a7bebb65e10899935126ba63dd51f": "“{0}”配置的选项属性必须是对象。",
|
||||
"779467f467862836e19f494a37d6ab77": "“{0}”配置的 acls 属性必须是对象数组。",
|
||||
"7bc7b301ad9c4fc873029d57fb9740fe": "查询 {1} 的 {0}。",
|
||||
"7c837b88fd0e509bd3fc722d7ddf0711": "{0} 的外键",
|
||||
"7d5e7ed0efaedf3f55f380caae0df8b8": "我的第一个移动应用程序",
|
||||
"7e0fca41d098607e1c9aa353c67e0fa1": "无效的访问令牌",
|
||||
"7e287fc885d9fdcf42da3a12f38572c1": "需要授权",
|
||||
"7ea04ea91aac3cb7ce0ddd96b7ff1fa4": "{{accessToken}} 需要注销",
|
||||
"80a32e80cbed65eba2103201a7c94710": "找不到模型:{0}",
|
||||
"830cb6c862f8f364e9064cea0026f701": "访存 hasOne 关系 {0}。",
|
||||
"83cbdc2560ba9f09155ccfc63e08f1a1": "无法针对“{1}”重新配置属性“{0}”。",
|
||||
"855eb8db89b4921c42072832d33d2dc2": "密码无效。",
|
||||
"855ecd4a64885ba272d782435f72a4d4": "未知的“{0}”标识“{1}”。",
|
||||
"860d1a0b8bd340411fb32baa72867989": "传输不支持 HTTP 重定向。",
|
||||
"86254879d01a60826a851066987703f2": "按标识添加 {0} 的相关项。",
|
||||
"895b1f941d026870b3cc8e6af087c197": "{{username}} 或 {{email}} 是必需的",
|
||||
"8a17c5ef611e2e7535792316e66b8fca": "密码过长:{0}",
|
||||
"8ae418c605b6a45f2651be9b1677c180": "无效的远程方法:“{0}”",
|
||||
"8bab6720ecc58ec6412358c858a53484": "批量更新失败,连接器已修改意外数量的记录:{0}",
|
||||
"8ecab7f534de38360bd1b1c88e880123": "“{0}”的子模型不会继承最新定义的远程方法 {1}。",
|
||||
"93ba9a1d03da3b7696332d3f155c5bb7": "\t HTML:{0}",
|
||||
"97795efe0c3eb7f35ce8cf8cfe70682b": "“{0}”的配置缺少 {{`dataSource`}} 属性。\n使用“null”或“false”来标记未附加到任何数据源的模型。",
|
||||
"9e3cbc1d5a9347cdcf6d1b4a6dbb55b7": "访存 belongsTo 关系 {0}。",
|
||||
"a50d10fc6e0959b220e085454c40381e": "找不到用户:{0}",
|
||||
"b6f740aeb6f2eb9bee9cb049dbfe6a28": "未知的“{0}”{{key}}“{1}”。",
|
||||
"ba96498b10c179f9cd75f75c8def4f70": "{{realm}} 是必需的",
|
||||
"c0057a569ff9d3b509bac61a4b2f605d": "删除此模型的所有 {0}。",
|
||||
"c2b5d51f007178170ca3952d59640ca4": "无法纠正 {0} 更改:\n{1}",
|
||||
"c4ee6d177c974532c3552d2f98eb72ea": "V3.0 中移除了 {0} 中间件。请参阅 {1} 以获取更多详细信息。",
|
||||
"c61a5a02ba3801a892308f70f5d55a14": "不推荐使用远程处理元数据 {{\"isStatic\"}}。而是针对 {{isStatic=false}} 在方法名称中指定 {{\"prototype.name\"}}。",
|
||||
"c68a93f0a9524fed4ff64372fc90c55f": "必须提供有效电子邮件",
|
||||
"cd0412f2f33a4a2a316acc834f3f21a6": "必须指定 {{id}} 或 {{data}}",
|
||||
"d5552322de5605c58b62f47ad26d2716": "已移除 {{`app.boot`}},请改用新模块 {{loopback-boot}}",
|
||||
"d6f43b266533b04d442bdb3955622592": "在此模型的 {0} 中创建新实例。",
|
||||
"da13d3cdf21330557254670dddd8c5c7": "计算 {0} 的数量({1})。",
|
||||
"dc568bee32deb0f6eaf63e73b20e8ceb": "忽略“{0}”的非对象“方法”设置。",
|
||||
"e4434de4bb8f5a3cd1d416e4d80d7e0b": "未知的“{0}”{{id}}“{1}”。",
|
||||
"e92aa25b6b864e3454b65a7c422bd114": "批量更新失败,连接器已删除意外数量的记录:{0}",
|
||||
|
@ -71,7 +87,7 @@
|
|||
"f0aed00a3d3d0b97d6594e4b70e0c201": "\t 传输:{0}",
|
||||
"f0bd73df8714cefb925e3b8da2f4c5f6": "结果:{0}",
|
||||
"f1d4ac54357cc0932f385d56814ba7e4": "冲突",
|
||||
"f58cdc481540cd1f69a4aa4da2e37981": "无效的密码:{0}",
|
||||
"f66ae3cf379b2fce28575a3282defe1a": "删除此模型的 {0}。",
|
||||
"f8e26bcca62a47f579562f1cd2c785ff": "请重新设计您的应用程序以使用正式解决方案插入来自请求上下文的“options”自变量,\n请参阅 {0}"
|
||||
}
|
||||
|
||||
|
|
|
@ -3,11 +3,13 @@
|
|||
"04bd8af876f001ceaf443aad6a9002f9": "需要定義模型 {0} 才能鑑別。",
|
||||
"095afbf2f1f0e5be678f5dac5c54e717": "拒絕存取",
|
||||
"0caffe1d763c8cca6a61814abe33b776": "需要電子郵件",
|
||||
"0e21aad369dd09e1965c11949303cefd": "{0}.{1} 的遠端 meta 資料 {{\"isStatic\"}} 不符合新的方法名稱型樣式。",
|
||||
"0da38687fed24275c1547e815914a8e3": "依 id 刪除 {0} 的相關項目。",
|
||||
"0e21aad369dd09e1965c11949303cefd": "{0} 的遠端 meta 資料。{1} {{\"isStatic\"}} 不符合新的方法名稱型樣式。",
|
||||
"10e01c895dc0b2fecc385f9f462f1ca6": "{{http://localhost:3000/colors}} 提供顏色清單",
|
||||
"1b2a6076dccbe91a56f1672eb3b8598c": "回應內文包含登入時建立的 {{AccessToken}} 的內容。\n根據 `include` 參數的值而定,內文還可能包含其他內容:\n\n - `user` - `U+007BUserU+007D` - 目前登入的使用者的資料。 {{(`include=user`)}}\n\n",
|
||||
"1b2a6076dccbe91a56f1672eb3b8598c": "回應內文包含登入時建立的 {{AccessToken}} 的內容。\n根據 `include` 參數的值而定,內文可能包含其他內容:\n\n - `user` - `U+007BUserU+007D` - 目前登入的使用者的資料。 {{(`include=user`)}}\n\n",
|
||||
"1d7833c3ca2f05fdad8fad7537531c40": "\t 主旨:{0}",
|
||||
"1e85f822b547a75d7d385048030e4ecb": "已建立:{0}",
|
||||
"22fe62fa8d595b72c62208beddaa2a56": "依 id 更新 {0} 的相關項目。",
|
||||
"275f22ab95671f095640ca99194b7635": "\t 寄件者:{0}",
|
||||
"2860bccdf9ef1e350c1a38932ed12173": "3.0 版中已移除 {0}。如需詳細資料,請參閱 {1}。",
|
||||
"2d3071e3b18681c80a090dc0efbdb349": "找不到 id 為 {1} 的 {0}",
|
||||
|
@ -16,62 +18,76 @@
|
|||
"3438fab56cc7ab92dfd88f0497e523e0": "`{0}` 配置的 relations 內容必須是物件",
|
||||
"3591f1d3e115b46f9f195df5ca548a6a": "找不到模型:模型 `{0}` 正在延伸不明模型 `{1}`。",
|
||||
"35e5252c62d80f8c54a5290d30f4c7d0": "請在 Web 瀏覽器中開啟此鏈結來驗證電子郵件:\n\t{0}",
|
||||
"37bcd4b50dfae98734772e39ffb1ea3d": "輸入的密碼太長。長度上限為 {0}(輸入了 {1})",
|
||||
"3aae63bb7e8e046641767571c1591441": "因為尚未驗證電子郵件,所以登入失敗",
|
||||
"3aecb24fa8bdd3f79d168761ca8a6729": "{{middleware}} 階段 {0} 不明",
|
||||
"3ca45aa6f705c46a4c598a900716f086": "{0} 正在使用模型設定,{1} 已不再可用。",
|
||||
"3ca45aa6f705c46a4c598a900716f086": "{0} 正在使用已不再可用的模型設定 {1}。",
|
||||
"3caaa84fc103d6d5612173ae6d43b245": "無效記號:{0}",
|
||||
"3d617953470be16d0c2b32f0bcfbb5ee": "感謝您登錄",
|
||||
"3d63008ccfb2af1db2142e8cc2716ace": "警告:未指定用於傳送電子郵件的電子郵件傳輸。請設定傳輸來傳送郵件訊息。",
|
||||
"4203ab415ec66a78d3164345439ba76e": "無法呼叫 {0}.{1}()。尚未設定 {2} 方法。{{PersistedModel}} 未正確連接至 {{DataSource}}!",
|
||||
"4203ab415ec66a78d3164345439ba76e": "無法呼叫 {0}。{1}()。尚未設定 {2} 方法。{{PersistedModel}} 未正確連接至 {{DataSource}}!",
|
||||
"42a36bac5cf03c4418d664500c81047a": "不再支援 {{DataSource}} 選項 {{\"defaultForType\"}}",
|
||||
"44a6c8b1ded4ed653d19ddeaaf89a606": "找不到電子郵件",
|
||||
"4a4f04a4e480fc5d4ee73b84d9a4b904": "正在傳送郵件:",
|
||||
"4b494de07f524703ac0879addbd64b13": "尚未驗證電子郵件",
|
||||
"528325f3cbf1b0ab9a08447515daac9a": "更新這個模型的 {0}。",
|
||||
"543d19bad5e47ee1e9eb8af688e857b4": "{0} 的外部索引鍵。",
|
||||
"57b87ae0e65f6ab7a2e3e6cbdfca49a4": "無法建立資料來源 {0}:{1}",
|
||||
"5858e63efaa0e4ad86b61c0459ea32fa": "您必須將 {{Email}} 模型連接至 {{Mail}} 連接器",
|
||||
"598ff0255ffd1d1b71e8de55dbe2c034": "依 id 檢查項目的 {0} 關係是否存在。",
|
||||
"5a36cc6ba0cc27c754f6c5ed6015ea3c": "依 id 移除項目的 {0} 關係。",
|
||||
"5e81ad3847a290dc650b47618b9cbc7e": "登入失敗",
|
||||
"5fa3afb425819ebde958043e598cb664": "找不到 {{id}} 為 {0} 的模型",
|
||||
"61e5deebaf44d68f4e6a508f30cc31a3": "模型 `{1}` 的關係 `{0}` 不存在",
|
||||
"62e8b0a733417978bab22c8dacf5d7e6": "無法套用大量更新,連接器未正確報告已更新的記錄數。",
|
||||
"63a091ced88001ab6acb58f61ec041c5": "\t 文字:{0}",
|
||||
"651f0b3cbba001635152ec3d3d954d0a": "依 id 尋找 {0} 的相關項目。",
|
||||
"6bc376432cd9972cf991aad3de371e78": "遺漏變更的資料:{0}",
|
||||
"705c2d456a3e204c4af56e671ec3225c": "找不到 {{accessToken}}",
|
||||
"734a7bebb65e10899935126ba63dd51f": "`{0}` 配置的 options 內容必須是物件",
|
||||
"779467f467862836e19f494a37d6ab77": "`{0}` 配置的 acls 內容必須是物件陣列",
|
||||
"7bc7b301ad9c4fc873029d57fb9740fe": "查詢 {0} 個(共 {1} 個)。",
|
||||
"7c837b88fd0e509bd3fc722d7ddf0711": "{0} 的外部索引鍵",
|
||||
"7d5e7ed0efaedf3f55f380caae0df8b8": "我的第一個行動式應用程式",
|
||||
"7e0fca41d098607e1c9aa353c67e0fa1": "存取記號無效",
|
||||
"7e287fc885d9fdcf42da3a12f38572c1": "需要授權",
|
||||
"7ea04ea91aac3cb7ce0ddd96b7ff1fa4": "需要 {{accessToken}} 才能登出",
|
||||
"80a32e80cbed65eba2103201a7c94710": "找不到模型:{0}",
|
||||
"830cb6c862f8f364e9064cea0026f701": "提取 hasOne 關係 {0}。",
|
||||
"83cbdc2560ba9f09155ccfc63e08f1a1": "無法為 `{1}` 重新配置內容 `{0}`",
|
||||
"855eb8db89b4921c42072832d33d2dc2": "密碼無效。",
|
||||
"855ecd4a64885ba272d782435f72a4d4": "\"{0}\" ID \"{1}\" 不明。",
|
||||
"860d1a0b8bd340411fb32baa72867989": "傳輸不支援 HTTP 重新導向。",
|
||||
"86254879d01a60826a851066987703f2": "依 id 新增 {0} 的相關項目。",
|
||||
"895b1f941d026870b3cc8e6af087c197": "需要 {{username}} 或 {{email}}",
|
||||
"8a17c5ef611e2e7535792316e66b8fca": "密碼太長:{0}",
|
||||
"8ae418c605b6a45f2651be9b1677c180": "無效的遠端方法:`{0}`",
|
||||
"8bab6720ecc58ec6412358c858a53484": "大量更新失敗,連接器已修改超乎預期的記錄數:{0}",
|
||||
"8ecab7f534de38360bd1b1c88e880123": "`{0}` 的子項模型將不會繼承新定義的遠端方法 {1}。",
|
||||
"93ba9a1d03da3b7696332d3f155c5bb7": "\t HTML:{0}",
|
||||
"97795efe0c3eb7f35ce8cf8cfe70682b": "`{0}` 的配置遺漏 {{`dataSource`}} 內容。\n請使用 `null` 或 `false` 來標示未連接至任何資料來源的模型。",
|
||||
"9e3cbc1d5a9347cdcf6d1b4a6dbb55b7": "提取 belongsTo 關係 {0}。",
|
||||
"a50d10fc6e0959b220e085454c40381e": "找不到使用者:{0}",
|
||||
"b6f740aeb6f2eb9bee9cb049dbfe6a28": "\"{0}\" {{key}} \"{1}\" 不明。",
|
||||
"ba96498b10c179f9cd75f75c8def4f70": "需要 {{realm}}",
|
||||
"c0057a569ff9d3b509bac61a4b2f605d": "刪除這個模型的所有 {0}。",
|
||||
"c2b5d51f007178170ca3952d59640ca4": "無法更正 {0} 個變更:\n{1}",
|
||||
"c4ee6d177c974532c3552d2f98eb72ea": "3.0 版中已移除 {0} 中介軟體。如需詳細資料,請參閱 {1}。",
|
||||
"c61a5a02ba3801a892308f70f5d55a14": "遠端 meta 資料 {{\"isStatic\"}} 已淘汰。請在方法名稱中指定 {{\"prototype.name\"}} 來代替 {{isStatic=false}}。",
|
||||
"c68a93f0a9524fed4ff64372fc90c55f": "必須提供有效的電子郵件",
|
||||
"cd0412f2f33a4a2a316acc834f3f21a6": "必須指定 {{id}} 或 {{data}}",
|
||||
"d5552322de5605c58b62f47ad26d2716": "已移除 {{`app.boot`}},請改用新的模組 {{loopback-boot}}",
|
||||
"d6f43b266533b04d442bdb3955622592": "在這個模型的 {0} 中建立新實例。",
|
||||
"da13d3cdf21330557254670dddd8c5c7": "計算 {0} 個(共 {1} 個)。",
|
||||
"dc568bee32deb0f6eaf63e73b20e8ceb": "忽略 \"{0}\" 的非物件 \"methods\" 設定。",
|
||||
"e4434de4bb8f5a3cd1d416e4d80d7e0b": "\"{0}\" {{id}} \"{1}\" 不明。",
|
||||
"e92aa25b6b864e3454b65a7c422bd114": "大量更新失敗,連接器已刪除非預期的記錄數:{0}",
|
||||
"ea63d226b6968e328bdf6876010786b5": "無法套用大量更新,連接器未正確報告已刪除的記錄數。",
|
||||
"ead044e2b4bce74b4357f8a03fb78ec4": "無法呼叫 {0}.{1}()。尚未設定 {2} 方法。{{KeyValueModel}} 未正確連接至 {{DataSource}}!",
|
||||
"ead044e2b4bce74b4357f8a03fb78ec4": "無法呼叫 {0}。{1}()。尚未設定 {2} 方法。{{KeyValueModel}} 未正確連接至 {{DataSource}}!",
|
||||
"ecb06666ef95e5db27a5ac1d6a17923b": "\t 收件者:{0}",
|
||||
"f0aed00a3d3d0b97d6594e4b70e0c201": "\t 傳輸:{0}",
|
||||
"f0bd73df8714cefb925e3b8da2f4c5f6": "結果:{0}",
|
||||
"f1d4ac54357cc0932f385d56814ba7e4": "衝突",
|
||||
"f58cdc481540cd1f69a4aa4da2e37981": "無效密碼:{0}",
|
||||
"f66ae3cf379b2fce28575a3282defe1a": "刪除這個模型的 {0}。",
|
||||
"f8e26bcca62a47f579562f1cd2c785ff": "請重做您的應用程式以使用正式解決方案,從要求環境定義注入 \"options\" 引數,\n請參閱 {0}"
|
||||
}
|
||||
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
// Copyright IBM Corp. 2014,2016. All Rights Reserved.
|
||||
// Copyright IBM Corp. 2014,2019. All Rights Reserved.
|
||||
// Node module: loopback
|
||||
// This file is licensed under the MIT License.
|
||||
// License text available at https://opensource.org/licenses/MIT
|
||||
|
||||
'use strict';
|
||||
var assert = require('assert');
|
||||
var loopback = require('./loopback');
|
||||
var debug = require('debug')('loopback:security:access-context');
|
||||
const assert = require('assert');
|
||||
const loopback = require('./loopback');
|
||||
const debug = require('debug')('loopback:security:access-context');
|
||||
|
||||
const DEFAULT_SCOPES = ['DEFAULT'];
|
||||
|
||||
|
@ -50,7 +50,7 @@ function AccessContext(context) {
|
|||
'Application registry is mandatory in AccessContext but missing in provided context');
|
||||
this.registry = context.registry;
|
||||
this.principals = context.principals || [];
|
||||
var model = context.model;
|
||||
let model = context.model;
|
||||
model = ('string' === typeof model) ? this.registry.getModel(model) : model;
|
||||
this.model = model;
|
||||
this.modelName = model && model.modelName;
|
||||
|
@ -77,20 +77,19 @@ function AccessContext(context) {
|
|||
'AccessToken model must be defined before AccessContext model');
|
||||
this.accessToken = context.accessToken || loopback.AccessToken.ANONYMOUS;
|
||||
|
||||
var principalType = context.principalType || Principal.USER;
|
||||
var principalId = context.principalId || undefined;
|
||||
var principalName = context.principalName || undefined;
|
||||
|
||||
if (principalId) {
|
||||
const principalType = context.principalType || Principal.USER;
|
||||
const principalId = context.principalId || undefined;
|
||||
const principalName = context.principalName || undefined;
|
||||
if (principalId != null) {
|
||||
this.addPrincipal(principalType, principalId, principalName);
|
||||
}
|
||||
|
||||
var token = this.accessToken || {};
|
||||
const token = this.accessToken;
|
||||
|
||||
if (token.userId) {
|
||||
this.addPrincipal(Principal.USER, token.userId);
|
||||
if (token.userId != null) {
|
||||
this.addPrincipal(token.principalType || Principal.USER, token.userId);
|
||||
}
|
||||
if (token.appId) {
|
||||
if (token.appId != null) {
|
||||
this.addPrincipal(Principal.APPLICATION, token.appId);
|
||||
}
|
||||
this.remotingContext = context.remotingContext;
|
||||
|
@ -127,9 +126,9 @@ AccessContext.permissionOrder = {
|
|||
* @returns {boolean}
|
||||
*/
|
||||
AccessContext.prototype.addPrincipal = function(principalType, principalId, principalName) {
|
||||
var principal = new Principal(principalType, principalId, principalName);
|
||||
for (var i = 0; i < this.principals.length; i++) {
|
||||
var p = this.principals[i];
|
||||
const principal = new Principal(principalType, principalId, principalName);
|
||||
for (let i = 0; i < this.principals.length; i++) {
|
||||
const p = this.principals[i];
|
||||
if (p.equals(principal)) {
|
||||
return false;
|
||||
}
|
||||
|
@ -143,7 +142,7 @@ AccessContext.prototype.addPrincipal = function(principalType, principalId, prin
|
|||
* @returns {*}
|
||||
*/
|
||||
AccessContext.prototype.getUserId = function() {
|
||||
var user = this.getUser();
|
||||
const user = this.getUser();
|
||||
return user && user.id;
|
||||
};
|
||||
|
||||
|
@ -152,10 +151,10 @@ AccessContext.prototype.getUserId = function() {
|
|||
* @returns {*}
|
||||
*/
|
||||
AccessContext.prototype.getUser = function() {
|
||||
var BaseUser = this.registry.getModel('User');
|
||||
for (var i = 0; i < this.principals.length; i++) {
|
||||
var p = this.principals[i];
|
||||
var isBuiltinPrincipal = p.type === Principal.APP ||
|
||||
const BaseUser = this.registry.getModel('User');
|
||||
for (let i = 0; i < this.principals.length; i++) {
|
||||
const p = this.principals[i];
|
||||
const isBuiltinPrincipal = p.type === Principal.APP ||
|
||||
p.type === Principal.ROLE ||
|
||||
p.type == Principal.SCOPE;
|
||||
if (isBuiltinPrincipal) continue;
|
||||
|
@ -166,7 +165,7 @@ AccessContext.prototype.getUser = function() {
|
|||
}
|
||||
|
||||
// or permit to resolve a valid user model
|
||||
var userModel = this.registry.findModel(p.type);
|
||||
const userModel = this.registry.findModel(p.type);
|
||||
if (!userModel) continue;
|
||||
if (userModel.prototype instanceof BaseUser) {
|
||||
return {id: p.id, principalType: p.type};
|
||||
|
@ -179,8 +178,8 @@ AccessContext.prototype.getUser = function() {
|
|||
* @returns {*}
|
||||
*/
|
||||
AccessContext.prototype.getAppId = function() {
|
||||
for (var i = 0; i < this.principals.length; i++) {
|
||||
var p = this.principals[i];
|
||||
for (let i = 0; i < this.principals.length; i++) {
|
||||
const p = this.principals[i];
|
||||
if (p.type === Principal.APPLICATION) {
|
||||
return p.id;
|
||||
}
|
||||
|
@ -193,7 +192,7 @@ AccessContext.prototype.getAppId = function() {
|
|||
* @returns {boolean}
|
||||
*/
|
||||
AccessContext.prototype.isAuthenticated = function() {
|
||||
return !!(this.getUserId() || this.getAppId());
|
||||
return this.getUserId() != null || this.getAppId() != null;
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -326,7 +325,7 @@ function AccessRequest(model, property, accessType, permission, methodNames, reg
|
|||
}
|
||||
if (arguments.length === 1 && typeof model === 'object') {
|
||||
// The argument is an object that contains all required properties
|
||||
var obj = model || {};
|
||||
const obj = model || {};
|
||||
this.model = obj.model || AccessContext.ALL;
|
||||
this.property = obj.property || AccessContext.ALL;
|
||||
this.accessType = obj.accessType || AccessContext.ALL;
|
||||
|
@ -364,10 +363,10 @@ AccessRequest.prototype.isWildcard = function() {
|
|||
*/
|
||||
|
||||
AccessRequest.prototype.exactlyMatches = function(acl) {
|
||||
var matchesModel = acl.model === this.model;
|
||||
var matchesProperty = acl.property === this.property;
|
||||
var matchesMethodName = this.methodNames.indexOf(acl.property) !== -1;
|
||||
var matchesAccessType = acl.accessType === this.accessType;
|
||||
const matchesModel = acl.model === this.model;
|
||||
const matchesProperty = acl.property === this.property;
|
||||
const matchesMethodName = this.methodNames.indexOf(acl.property) !== -1;
|
||||
const matchesAccessType = acl.accessType === this.accessType;
|
||||
|
||||
if (matchesModel && matchesAccessType) {
|
||||
return matchesProperty || matchesMethodName;
|
||||
|
@ -389,9 +388,9 @@ AccessRequest.prototype.settleDefaultPermission = function(defaultPermission) {
|
|||
if (this.permission !== 'DEFAULT')
|
||||
return;
|
||||
|
||||
var modelName = this.model;
|
||||
const modelName = this.model;
|
||||
if (!defaultPermission) {
|
||||
var modelClass = this.registry.findModel(modelName);
|
||||
const modelClass = this.registry.findModel(modelName);
|
||||
defaultPermission = modelClass && modelClass.settings.defaultPermission;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// Copyright IBM Corp. 2013,2016. All Rights Reserved.
|
||||
// Copyright IBM Corp. 2013,2019. All Rights Reserved.
|
||||
// Node module: loopback
|
||||
// This file is licensed under the MIT License.
|
||||
// License text available at https://opensource.org/licenses/MIT
|
||||
|
@ -8,17 +8,17 @@
|
|||
*/
|
||||
|
||||
'use strict';
|
||||
var g = require('./globalize');
|
||||
var DataSource = require('loopback-datasource-juggler').DataSource;
|
||||
var Registry = require('./registry');
|
||||
var assert = require('assert');
|
||||
var fs = require('fs');
|
||||
var extend = require('util')._extend;
|
||||
var RemoteObjects = require('strong-remoting');
|
||||
var classify = require('underscore.string/classify');
|
||||
var camelize = require('underscore.string/camelize');
|
||||
var path = require('path');
|
||||
var util = require('util');
|
||||
const g = require('./globalize');
|
||||
const DataSource = require('loopback-datasource-juggler').DataSource;
|
||||
const Registry = require('./registry');
|
||||
const assert = require('assert');
|
||||
const fs = require('fs');
|
||||
const extend = require('util')._extend;
|
||||
const RemoteObjects = require('strong-remoting');
|
||||
const classify = require('underscore.string/classify');
|
||||
const camelize = require('underscore.string/camelize');
|
||||
const path = require('path');
|
||||
const util = require('util');
|
||||
|
||||
/**
|
||||
* The `App` object represents a Loopback application.
|
||||
|
@ -49,7 +49,7 @@ function App() {
|
|||
* Export the app prototype.
|
||||
*/
|
||||
|
||||
var app = module.exports = {};
|
||||
const app = module.exports = {};
|
||||
|
||||
/**
|
||||
* Lazily load a set of [remote objects](http://apidocs.strongloop.com/strong-remoting/#remoteobjectsoptions).
|
||||
|
@ -62,7 +62,7 @@ app.remotes = function() {
|
|||
if (this._remotes) {
|
||||
return this._remotes;
|
||||
} else {
|
||||
var options = {};
|
||||
let options = {};
|
||||
|
||||
if (this.get) {
|
||||
options = this.get('remoting');
|
||||
|
@ -78,7 +78,7 @@ app.remotes = function() {
|
|||
|
||||
app.disuse = function(route) {
|
||||
if (this.stack) {
|
||||
for (var i = 0; i < this.stack.length; i++) {
|
||||
for (let i = 0; i < this.stack.length; i++) {
|
||||
if (this.stack[i].route === route) {
|
||||
this.stack.splice(i, 1);
|
||||
}
|
||||
|
@ -111,11 +111,11 @@ app.disuse = function(route) {
|
|||
*/
|
||||
|
||||
app.model = function(Model, config) {
|
||||
var isPublic = true;
|
||||
var registry = this.registry;
|
||||
let isPublic = true;
|
||||
const registry = this.registry;
|
||||
|
||||
if (typeof Model === 'string') {
|
||||
var msg = 'app.model(modelName, settings) is no longer supported. ' +
|
||||
const msg = 'app.model(modelName, settings) is no longer supported. ' +
|
||||
'Use app.registry.createModel(modelName, definition) and ' +
|
||||
'app.model(ModelCtor, config) instead.';
|
||||
throw new Error(msg);
|
||||
|
@ -130,7 +130,7 @@ app.model = function(Model, config) {
|
|||
Model.modelName + ' must be a descendant of loopback.Model');
|
||||
}
|
||||
|
||||
var modelName = Model.modelName;
|
||||
const modelName = Model.modelName;
|
||||
this.models[modelName] =
|
||||
this.models[classify(modelName)] =
|
||||
this.models[camelize(modelName)] = Model;
|
||||
|
@ -149,11 +149,13 @@ app.model = function(Model, config) {
|
|||
this.emit('modelRemoted', Model.sharedClass);
|
||||
}
|
||||
|
||||
var self = this;
|
||||
const self = this;
|
||||
Model.on('remoteMethodDisabled', function(model, methodName) {
|
||||
clearHandlerCache(self);
|
||||
self.emit('remoteMethodDisabled', model, methodName);
|
||||
});
|
||||
Model.on('remoteMethodAdded', function(model) {
|
||||
clearHandlerCache(self);
|
||||
self.emit('remoteMethodAdded', model);
|
||||
});
|
||||
|
||||
|
@ -163,6 +165,44 @@ app.model = function(Model, config) {
|
|||
return Model;
|
||||
};
|
||||
|
||||
/**
|
||||
* Remove all references to a previously registered Model.
|
||||
*
|
||||
* The method emits "modelDeleted" event as a counter-part to "modelRemoted"
|
||||
* event.
|
||||
*
|
||||
* @param {String} modelName The name of the model to remove.
|
||||
*/
|
||||
app.deleteModelByName = function(modelName) {
|
||||
const ModelCtor = this.models[modelName];
|
||||
delete this.models[modelName];
|
||||
delete this.models[classify(modelName)];
|
||||
delete this.models[camelize(modelName)];
|
||||
|
||||
if (ModelCtor) {
|
||||
ModelCtor.removeAllListeners();
|
||||
|
||||
const ix = this._models.indexOf(ModelCtor);
|
||||
if (ix > -1) {
|
||||
this._models.splice(ix, 1);
|
||||
}
|
||||
}
|
||||
|
||||
const remotes = this.remotes();
|
||||
remotes.deleteClassByName(modelName);
|
||||
remotes.deleteTypeByName(modelName);
|
||||
|
||||
if (ModelCtor && ModelCtor.dataSource) {
|
||||
ModelCtor.dataSource.deleteModelByName(modelName);
|
||||
} else {
|
||||
this.registry.modelBuilder.deleteModelByName(modelName);
|
||||
}
|
||||
|
||||
clearHandlerCache(this);
|
||||
|
||||
this.emit('modelDeleted', ModelCtor || modelName);
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the models exported by the app. Returns only models defined using `app.model()`
|
||||
*
|
||||
|
@ -226,7 +266,7 @@ app.models = function() {
|
|||
*/
|
||||
app.dataSource = function(name, config) {
|
||||
try {
|
||||
var ds = dataSourcesFromConfig(name, config, this.connectors, this.registry);
|
||||
const ds = dataSourcesFromConfig(name, config, this.connectors, this.registry);
|
||||
this.dataSources[name] =
|
||||
this.dataSources[classify(name)] =
|
||||
this.dataSources[camelize(name)] = ds;
|
||||
|
@ -267,7 +307,7 @@ app.connector = function(name, connector) {
|
|||
*/
|
||||
|
||||
app.remoteObjects = function() {
|
||||
var result = {};
|
||||
const result = {};
|
||||
|
||||
this.remotes().classes().forEach(function(sharedClass) {
|
||||
result[sharedClass.name] = sharedClass.ctor;
|
||||
|
@ -282,13 +322,13 @@ app.remoteObjects = function() {
|
|||
*/
|
||||
|
||||
app.handler = function(type, options) {
|
||||
var handlers = this._handlers || (this._handlers = {});
|
||||
const handlers = this._handlers || (this._handlers = {});
|
||||
if (handlers[type]) {
|
||||
return handlers[type];
|
||||
}
|
||||
|
||||
var remotes = this.remotes();
|
||||
var handler = this._handlers[type] = remotes.handler(type, options);
|
||||
const remotes = this.remotes();
|
||||
const handler = this._handlers[type] = remotes.handler(type, options);
|
||||
|
||||
remotes.classes().forEach(function(sharedClass) {
|
||||
sharedClass.ctor.emit('mounted', app, sharedClass, remotes);
|
||||
|
@ -308,28 +348,29 @@ app.dataSources = app.datasources = {};
|
|||
*/
|
||||
|
||||
app.enableAuth = function(options) {
|
||||
var AUTH_MODELS = ['User', 'AccessToken', 'ACL', 'Role', 'RoleMapping'];
|
||||
const AUTH_MODELS = ['User', 'AccessToken', 'ACL', 'Role', 'RoleMapping'];
|
||||
|
||||
var remotes = this.remotes();
|
||||
var app = this;
|
||||
const remotes = this.remotes();
|
||||
const app = this;
|
||||
|
||||
if (options && options.dataSource) {
|
||||
var appModels = app.registry.modelBuilder.models;
|
||||
const appModels = app.registry.modelBuilder.models;
|
||||
AUTH_MODELS.forEach(function(m) {
|
||||
var Model = app.registry.findModel(m);
|
||||
const Model = app.registry.findModel(m);
|
||||
if (!Model) {
|
||||
throw new Error(
|
||||
g.f('Authentication requires model %s to be defined.', m));
|
||||
g.f('Authentication requires model %s to be defined.', m),
|
||||
);
|
||||
}
|
||||
|
||||
if (Model.dataSource || Model.app) return;
|
||||
|
||||
// Find descendants of Model that are attached,
|
||||
// for example "Customer" extending "User" model
|
||||
for (var name in appModels) {
|
||||
var candidate = appModels[name];
|
||||
var isSubclass = candidate.prototype instanceof Model;
|
||||
var isAttached = !!candidate.dataSource || !!candidate.app;
|
||||
for (const name in appModels) {
|
||||
const candidate = appModels[name];
|
||||
const isSubclass = candidate.prototype instanceof Model;
|
||||
const isAttached = !!candidate.dataSource || !!candidate.app;
|
||||
if (isSubclass && isAttached) return;
|
||||
}
|
||||
|
||||
|
@ -341,22 +382,22 @@ app.enableAuth = function(options) {
|
|||
}
|
||||
|
||||
remotes.authorization = function(ctx, next) {
|
||||
var method = ctx.method;
|
||||
var req = ctx.req;
|
||||
var Model = method.ctor;
|
||||
var modelInstance = ctx.instance;
|
||||
const method = ctx.method;
|
||||
const req = ctx.req;
|
||||
const Model = method.ctor;
|
||||
const modelInstance = ctx.instance;
|
||||
|
||||
var modelId = modelInstance && modelInstance.id ||
|
||||
const modelId = modelInstance && modelInstance.id ||
|
||||
// replacement for deprecated req.param()
|
||||
(req.params && req.params.id !== undefined ? req.params.id :
|
||||
req.body && req.body.id !== undefined ? req.body.id :
|
||||
req.query && req.query.id !== undefined ? req.query.id :
|
||||
undefined);
|
||||
req.body && req.body.id !== undefined ? req.body.id :
|
||||
req.query && req.query.id !== undefined ? req.query.id :
|
||||
undefined);
|
||||
|
||||
var modelName = Model.modelName;
|
||||
const modelName = Model.modelName;
|
||||
|
||||
var modelSettings = Model.settings || {};
|
||||
var errStatusCode = modelSettings.aclErrorStatus || app.get('aclErrorStatus') || 401;
|
||||
const modelSettings = Model.settings || {};
|
||||
let errStatusCode = modelSettings.aclErrorStatus || app.get('aclErrorStatus') || 401;
|
||||
if (!req.accessToken) {
|
||||
errStatusCode = 401;
|
||||
}
|
||||
|
@ -374,7 +415,7 @@ app.enableAuth = function(options) {
|
|||
} else if (allowed) {
|
||||
next();
|
||||
} else {
|
||||
var messages = {
|
||||
const messages = {
|
||||
403: {
|
||||
message: g.f('Access Denied'),
|
||||
code: 'ACCESS_DENIED',
|
||||
|
@ -389,12 +430,12 @@ app.enableAuth = function(options) {
|
|||
},
|
||||
};
|
||||
|
||||
var e = new Error(messages[errStatusCode].message || messages[403].message);
|
||||
const e = new Error(messages[errStatusCode].message || messages[403].message);
|
||||
e.statusCode = errStatusCode;
|
||||
e.code = messages[errStatusCode].code || messages[403].code;
|
||||
next(e);
|
||||
}
|
||||
}
|
||||
},
|
||||
);
|
||||
} else {
|
||||
next();
|
||||
|
@ -445,7 +486,8 @@ app._verifyAuthModelRelations = function() {
|
|||
'custom User subclass, but does not fix AccessToken relations ' +
|
||||
'to use this new model.\n' +
|
||||
'Learn more at http://ibm.biz/setup-loopback-auth',
|
||||
Model.modelName, userName, userName);
|
||||
Model.modelName, userName, userName,
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -453,12 +495,25 @@ app._verifyAuthModelRelations = function() {
|
|||
'The model %j does not have "belongsTo User-like model" relation ' +
|
||||
'configured.\n' +
|
||||
'Learn more at http://ibm.biz/setup-loopback-auth',
|
||||
Model.modelName);
|
||||
Model.modelName,
|
||||
);
|
||||
}
|
||||
|
||||
function verifyUserRelations(Model) {
|
||||
const hasManyTokens = Model.relations && Model.relations.accessTokens;
|
||||
if (hasManyTokens) return;
|
||||
|
||||
if (hasManyTokens) {
|
||||
// display a temp warning message for users using multiple users config
|
||||
if (hasManyTokens.polymorphic) {
|
||||
console.warn(
|
||||
'The app configuration follows the multiple user models setup ' +
|
||||
'as described in http://ibm.biz/setup-loopback-auth',
|
||||
'The built-in role resolver $owner is not currently compatible ' +
|
||||
'with this configuration and should not be used in production.',
|
||||
);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
const relationsConfig = Model.settings.relations || {};
|
||||
const accessTokenName = (relationsConfig.accessTokens || {}).model;
|
||||
|
@ -471,7 +526,8 @@ app._verifyAuthModelRelations = function() {
|
|||
'AccessToken subclass, but does not fix User relations to use this ' +
|
||||
'new model.\n' +
|
||||
'Learn more at http://ibm.biz/setup-loopback-auth',
|
||||
Model.modelName, accessTokenName, accessTokenName);
|
||||
Model.modelName, accessTokenName, accessTokenName,
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -479,44 +535,46 @@ app._verifyAuthModelRelations = function() {
|
|||
'The model %j does not have "hasMany AccessToken-like models" relation ' +
|
||||
'configured.\n' +
|
||||
'Learn more at http://ibm.biz/setup-loopback-auth',
|
||||
Model.modelName);
|
||||
Model.modelName,
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
app.boot = function(options) {
|
||||
throw new Error(
|
||||
g.f('{{`app.boot`}} was removed, use the new module {{loopback-boot}} instead'));
|
||||
g.f('{{`app.boot`}} was removed, use the new module {{loopback-boot}} instead'),
|
||||
);
|
||||
};
|
||||
|
||||
function dataSourcesFromConfig(name, config, connectorRegistry, registry) {
|
||||
var connectorPath;
|
||||
let connectorPath;
|
||||
|
||||
assert(typeof config === 'object',
|
||||
'can not create data source without config object');
|
||||
|
||||
if (typeof config.connector === 'string') {
|
||||
name = config.connector;
|
||||
if (connectorRegistry[name]) {
|
||||
config.connector = connectorRegistry[name];
|
||||
const connectorName = config.connector;
|
||||
if (connectorRegistry[connectorName]) {
|
||||
config.connector = connectorRegistry[connectorName];
|
||||
} else {
|
||||
connectorPath = path.join(__dirname, 'connectors', name + '.js');
|
||||
connectorPath = path.join(__dirname, 'connectors', connectorName + '.js');
|
||||
|
||||
if (fs.existsSync(connectorPath)) {
|
||||
config.connector = require(connectorPath);
|
||||
}
|
||||
}
|
||||
if (config.connector && typeof config.connector === 'object' && !config.connector.name)
|
||||
config.connector.name = name;
|
||||
config.connector.name = connectorName;
|
||||
}
|
||||
|
||||
return registry.createDataSource(config);
|
||||
return registry.createDataSource(name, config);
|
||||
}
|
||||
|
||||
function configureModel(ModelCtor, config, app) {
|
||||
assert(ModelCtor.prototype instanceof ModelCtor.registry.getModel('Model'),
|
||||
ModelCtor.modelName + ' must be a descendant of loopback.Model');
|
||||
|
||||
var dataSource = config.dataSource;
|
||||
let dataSource = config.dataSource;
|
||||
|
||||
if (dataSource) {
|
||||
if (typeof dataSource === 'string') {
|
||||
|
@ -526,7 +584,7 @@ function configureModel(ModelCtor, config, app) {
|
|||
assert(
|
||||
dataSource instanceof DataSource,
|
||||
ModelCtor.modelName + ' is referencing a dataSource that does not exist: "' +
|
||||
config.dataSource + '"'
|
||||
config.dataSource + '"',
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -534,85 +592,12 @@ function configureModel(ModelCtor, config, app) {
|
|||
config.dataSource = dataSource;
|
||||
|
||||
app.registry.configureModel(ModelCtor, config);
|
||||
|
||||
setSharedMethodSharedProperties(ModelCtor, app, config);
|
||||
}
|
||||
|
||||
function setSharedMethodSharedProperties(model, app, modelConfigs) {
|
||||
var settings = {};
|
||||
|
||||
// apply config.json settings
|
||||
var config = app.get('remoting');
|
||||
var configHasSharedMethodsSettings = config &&
|
||||
config.sharedMethods &&
|
||||
typeof config.sharedMethods === 'object';
|
||||
if (configHasSharedMethodsSettings)
|
||||
util._extend(settings, config.sharedMethods);
|
||||
|
||||
// apply model-config.json settings
|
||||
var modelConfig = modelConfigs.options;
|
||||
var modelConfigHasSharedMethodsSettings = modelConfig &&
|
||||
modelConfig.remoting &&
|
||||
modelConfig.remoting.sharedMethods &&
|
||||
typeof modelConfig.remoting.sharedMethods === 'object';
|
||||
if (modelConfigHasSharedMethodsSettings)
|
||||
util._extend(settings, modelConfig.remoting.sharedMethods);
|
||||
|
||||
// validate setting values
|
||||
Object.keys(settings).forEach(function(setting) {
|
||||
var settingValue = settings[setting];
|
||||
var settingValueType = typeof settingValue;
|
||||
if (settingValueType !== 'boolean')
|
||||
throw new TypeError(g.f('Expected boolean, got %s', settingValueType));
|
||||
});
|
||||
|
||||
// set sharedMethod.shared using the merged settings
|
||||
var sharedMethods = model.sharedClass.methods({includeDisabled: true});
|
||||
|
||||
// re-map glob style values to regular expressions
|
||||
var tests = Object
|
||||
.keys(settings)
|
||||
.filter(function(setting) {
|
||||
return settings.hasOwnProperty(setting) && setting.indexOf('*') >= 0;
|
||||
})
|
||||
.map(function(setting) {
|
||||
// Turn * into an testable regexp string
|
||||
var glob = escapeRegExp(setting).replace(/\*/g, '(.)*');
|
||||
return {regex: new RegExp(glob), setting: settings[setting]};
|
||||
}) || [];
|
||||
sharedMethods.forEach(function(sharedMethod) {
|
||||
// use the specific setting if it exists
|
||||
var methodName = sharedMethod.isStatic ? sharedMethod.name : 'prototype.' + sharedMethod.name;
|
||||
var hasSpecificSetting = settings.hasOwnProperty(methodName);
|
||||
if (hasSpecificSetting) {
|
||||
if (settings[methodName] === false) {
|
||||
sharedMethod.sharedClass.disableMethodByName(methodName);
|
||||
} else {
|
||||
sharedMethod.shared = true;
|
||||
}
|
||||
} else {
|
||||
tests.forEach(function(glob) {
|
||||
if (glob.regex.test(methodName)) {
|
||||
if (glob.setting === false) {
|
||||
sharedMethod.sharedClass.disableMethodByName(methodName);
|
||||
} else {
|
||||
sharedMethod.shared = true;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function clearHandlerCache(app) {
|
||||
app._handlers = undefined;
|
||||
}
|
||||
|
||||
// Sanitize all RegExp reserved characters except * for pattern gobbing
|
||||
function escapeRegExp(str) {
|
||||
return str.replace(/[\-\[\]\/\{\}\(\)\+\?\.\\\^\$\|]/g, '\\$&');
|
||||
}
|
||||
|
||||
/**
|
||||
* Listen for connections and update the configured port.
|
||||
*
|
||||
|
@ -642,15 +627,15 @@ function escapeRegExp(str) {
|
|||
* as the request handler.
|
||||
*/
|
||||
app.listen = function(cb) {
|
||||
var self = this;
|
||||
const self = this;
|
||||
|
||||
var server = require('http').createServer(this);
|
||||
const server = require('http').createServer(this);
|
||||
|
||||
server.on('listening', function() {
|
||||
self.set('port', this.address().port);
|
||||
|
||||
var listeningOnAll = false;
|
||||
var host = self.get('host');
|
||||
let listeningOnAll = false;
|
||||
let host = self.get('host');
|
||||
if (!host) {
|
||||
listeningOnAll = true;
|
||||
host = this.address().address;
|
||||
|
@ -665,17 +650,17 @@ app.listen = function(cb) {
|
|||
// that can be copied and pasted into the browser.
|
||||
host = 'localhost';
|
||||
}
|
||||
var url = 'http://' + host + ':' + self.get('port') + '/';
|
||||
const url = 'http://' + host + ':' + self.get('port') + '/';
|
||||
self.set('url', url);
|
||||
}
|
||||
});
|
||||
|
||||
var useAppConfig =
|
||||
const useAppConfig =
|
||||
arguments.length === 0 ||
|
||||
(arguments.length == 1 && typeof arguments[0] == 'function');
|
||||
|
||||
if (useAppConfig) {
|
||||
var port = this.get('port');
|
||||
let port = this.get('port');
|
||||
// NOTE(bajtos) port:undefined no longer works on node@6,
|
||||
// we must pass port:0 explicitly
|
||||
if (port === undefined) port = 0;
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
// Copyright IBM Corp. 2014. All Rights Reserved.
|
||||
// Copyright IBM Corp. 2014,2019. All Rights Reserved.
|
||||
// Node module: loopback
|
||||
// This file is licensed under the MIT License.
|
||||
// License text available at https://opensource.org/licenses/MIT
|
||||
|
||||
'use strict';
|
||||
var EventEmitter = require('events').EventEmitter;
|
||||
var util = require('util');
|
||||
const EventEmitter = require('events').EventEmitter;
|
||||
const util = require('util');
|
||||
|
||||
module.exports = browserExpress;
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// Copyright IBM Corp. 2014,2016. All Rights Reserved.
|
||||
// Copyright IBM Corp. 2014,2019. All Rights Reserved.
|
||||
// Node module: loopback
|
||||
// This file is licensed under the MIT License.
|
||||
// License text available at https://opensource.org/licenses/MIT
|
||||
|
@ -12,47 +12,58 @@ module.exports = function(registry) {
|
|||
|
||||
registry.KeyValueModel = createModel(
|
||||
require('../common/models/key-value-model.json'),
|
||||
require('../common/models/key-value-model.js'));
|
||||
require('../common/models/key-value-model.js'),
|
||||
);
|
||||
|
||||
registry.Email = createModel(
|
||||
require('../common/models/email.json'),
|
||||
require('../common/models/email.js'));
|
||||
require('../common/models/email.js'),
|
||||
);
|
||||
|
||||
registry.Application = createModel(
|
||||
require('../common/models/application.json'),
|
||||
require('../common/models/application.js'));
|
||||
require('../common/models/application.js'),
|
||||
);
|
||||
|
||||
registry.AccessToken = createModel(
|
||||
require('../common/models/access-token.json'),
|
||||
require('../common/models/access-token.js'));
|
||||
require('../common/models/access-token.js'),
|
||||
);
|
||||
|
||||
registry.User = createModel(
|
||||
require('../common/models/user.json'),
|
||||
require('../common/models/user.js'));
|
||||
require('../common/models/user.js'),
|
||||
);
|
||||
|
||||
registry.RoleMapping = createModel(
|
||||
require('../common/models/role-mapping.json'),
|
||||
require('../common/models/role-mapping.js'));
|
||||
require('../common/models/role-mapping.js'),
|
||||
);
|
||||
|
||||
registry.Role = createModel(
|
||||
require('../common/models/role.json'),
|
||||
require('../common/models/role.js'));
|
||||
require('../common/models/role.js'),
|
||||
);
|
||||
|
||||
registry.ACL = createModel(
|
||||
require('../common/models/acl.json'),
|
||||
require('../common/models/acl.js'));
|
||||
require('../common/models/acl.js'),
|
||||
);
|
||||
|
||||
registry.Scope = createModel(
|
||||
require('../common/models/scope.json'),
|
||||
require('../common/models/scope.js'));
|
||||
require('../common/models/scope.js'),
|
||||
);
|
||||
|
||||
registry.Change = createModel(
|
||||
require('../common/models/change.json'),
|
||||
require('../common/models/change.js'));
|
||||
require('../common/models/change.js'),
|
||||
);
|
||||
|
||||
registry.Checkpoint = createModel(
|
||||
require('../common/models/checkpoint.json'),
|
||||
require('../common/models/checkpoint.js'));
|
||||
require('../common/models/checkpoint.js'),
|
||||
);
|
||||
|
||||
function createModel(definitionJson, customizeFn) {
|
||||
// Clone the JSON definition to allow applications
|
||||
|
@ -63,7 +74,7 @@ module.exports = function(registry) {
|
|||
// object instance it loaded during the first call.
|
||||
definitionJson = cloneDeepJson(definitionJson);
|
||||
|
||||
var Model = registry.createModel(definitionJson);
|
||||
const Model = registry.createModel(definitionJson);
|
||||
customizeFn(Model);
|
||||
return Model;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,80 @@
|
|||
// Copyright IBM Corp. 2017,2019. All Rights Reserved.
|
||||
// Node module: loopback
|
||||
// This file is licensed under the MIT License.
|
||||
// License text available at https://opensource.org/licenses/MIT
|
||||
|
||||
'use strict';
|
||||
|
||||
const util = require('util');
|
||||
const extend = require('util')._extend;
|
||||
const g = require('./globalize');
|
||||
|
||||
module.exports = function(modelCtor, remotingConfig, modelConfig) {
|
||||
const settings = {};
|
||||
|
||||
// apply config.json settings
|
||||
const configHasSharedMethodsSettings = remotingConfig &&
|
||||
remotingConfig.sharedMethods &&
|
||||
typeof remotingConfig.sharedMethods === 'object';
|
||||
if (configHasSharedMethodsSettings)
|
||||
util._extend(settings, remotingConfig.sharedMethods);
|
||||
|
||||
// apply model-config.json settings
|
||||
const options = modelConfig.options;
|
||||
const modelConfigHasSharedMethodsSettings = options &&
|
||||
options.remoting &&
|
||||
options.remoting.sharedMethods &&
|
||||
typeof options.remoting.sharedMethods === 'object';
|
||||
if (modelConfigHasSharedMethodsSettings)
|
||||
util._extend(settings, options.remoting.sharedMethods);
|
||||
|
||||
// validate setting values
|
||||
Object.keys(settings).forEach(function(setting) {
|
||||
const settingValue = settings[setting];
|
||||
const settingValueType = typeof settingValue;
|
||||
if (settingValueType !== 'boolean')
|
||||
throw new TypeError(g.f('Expected boolean, got %s', settingValueType));
|
||||
});
|
||||
|
||||
// set sharedMethod.shared using the merged settings
|
||||
const sharedMethods = modelCtor.sharedClass.methods({includeDisabled: true});
|
||||
|
||||
// re-map glob style values to regular expressions
|
||||
const tests = Object
|
||||
.keys(settings)
|
||||
.filter(function(setting) {
|
||||
return settings.hasOwnProperty(setting) && setting.indexOf('*') >= 0;
|
||||
})
|
||||
.map(function(setting) {
|
||||
// Turn * into an testable regexp string
|
||||
const glob = escapeRegExp(setting).replace(/\*/g, '(.)*');
|
||||
return {regex: new RegExp(glob), setting: settings[setting]};
|
||||
}) || [];
|
||||
sharedMethods.forEach(function(sharedMethod) {
|
||||
// use the specific setting if it exists
|
||||
const methodName = sharedMethod.isStatic ? sharedMethod.name : 'prototype.' + sharedMethod.name;
|
||||
const hasSpecificSetting = settings.hasOwnProperty(methodName);
|
||||
if (hasSpecificSetting) {
|
||||
if (settings[methodName] === false) {
|
||||
sharedMethod.sharedClass.disableMethodByName(methodName);
|
||||
} else {
|
||||
sharedMethod.shared = true;
|
||||
}
|
||||
} else {
|
||||
tests.forEach(function(glob) {
|
||||
if (glob.regex.test(methodName)) {
|
||||
if (glob.setting === false) {
|
||||
sharedMethod.sharedClass.disableMethodByName(methodName);
|
||||
} else {
|
||||
sharedMethod.shared = true;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
// Sanitize all RegExp reserved characters except * for pattern gobbing
|
||||
function escapeRegExp(str) {
|
||||
return str.replace(/[\-\[\]\/\{\}\(\)\+\?\.\\\^\$\|]/g, '\\$&');
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
// Copyright IBM Corp. 2013,2014. All Rights Reserved.
|
||||
// Copyright IBM Corp. 2013,2019. All Rights Reserved.
|
||||
// Node module: loopback
|
||||
// This file is licensed under the MIT License.
|
||||
// License text available at https://opensource.org/licenses/MIT
|
||||
|
@ -14,11 +14,11 @@ module.exports = Connector;
|
|||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var EventEmitter = require('events').EventEmitter;
|
||||
var debug = require('debug')('connector');
|
||||
var util = require('util');
|
||||
var inherits = util.inherits;
|
||||
var assert = require('assert');
|
||||
const EventEmitter = require('events').EventEmitter;
|
||||
const debug = require('debug')('connector');
|
||||
const util = require('util');
|
||||
const inherits = util.inherits;
|
||||
const assert = require('assert');
|
||||
|
||||
/**
|
||||
* Create a new `Connector` with the given `options`.
|
||||
|
@ -45,7 +45,7 @@ inherits(Connector, EventEmitter);
|
|||
*/
|
||||
|
||||
Connector._createJDBAdapter = function(jdbModule) {
|
||||
var fauxSchema = {};
|
||||
const fauxSchema = {};
|
||||
jdbModule.initialize(fauxSchema, function() {
|
||||
// connected
|
||||
});
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// Copyright IBM Corp. 2013,2016. All Rights Reserved.
|
||||
// Copyright IBM Corp. 2013,2019. All Rights Reserved.
|
||||
// Node module: loopback
|
||||
// This file is licensed under the MIT License.
|
||||
// License text available at https://opensource.org/licenses/MIT
|
||||
|
@ -8,11 +8,11 @@
|
|||
*/
|
||||
|
||||
'use strict';
|
||||
var g = require('../globalize');
|
||||
var mailer = require('nodemailer');
|
||||
var assert = require('assert');
|
||||
var debug = require('debug')('loopback:connector:mail');
|
||||
var loopback = require('../loopback');
|
||||
const g = require('../globalize');
|
||||
const mailer = require('nodemailer');
|
||||
const assert = require('assert');
|
||||
const debug = require('debug')('loopback:connector:mail');
|
||||
const loopback = require('../loopback');
|
||||
|
||||
/**
|
||||
* Export the MailConnector class.
|
||||
|
@ -27,7 +27,7 @@ module.exports = MailConnector;
|
|||
function MailConnector(settings) {
|
||||
assert(typeof settings === 'object', 'cannot initialize MailConnector without a settings object');
|
||||
|
||||
var transports = settings.transports;
|
||||
let transports = settings.transports;
|
||||
|
||||
// if transports is not in settings object AND settings.transport exists
|
||||
if (!transports && settings.transport) {
|
||||
|
@ -74,19 +74,17 @@ MailConnector.prototype.DataAccessObject = Mailer;
|
|||
*/
|
||||
|
||||
MailConnector.prototype.setupTransport = function(setting) {
|
||||
var connector = this;
|
||||
const connector = this;
|
||||
connector.transports = connector.transports || [];
|
||||
connector.transportsIndex = connector.transportsIndex || {};
|
||||
|
||||
var transport;
|
||||
var transportType = (setting.type || 'STUB').toLowerCase();
|
||||
if (transportType === 'direct') {
|
||||
transport = mailer.createTransport();
|
||||
} else if (transportType === 'smtp') {
|
||||
let transport;
|
||||
const transportType = (setting.type || 'STUB').toLowerCase();
|
||||
if (transportType === 'smtp') {
|
||||
transport = mailer.createTransport(setting);
|
||||
} else {
|
||||
var transportModuleName = 'nodemailer-' + transportType + '-transport';
|
||||
var transportModule = require(transportModuleName);
|
||||
const transportModuleName = 'nodemailer-' + transportType + '-transport';
|
||||
const transportModule = require(transportModuleName);
|
||||
transport = mailer.createTransport(transportModule(setting));
|
||||
}
|
||||
|
||||
|
@ -140,12 +138,12 @@ MailConnector.prototype.defaultTransport = function() {
|
|||
*/
|
||||
|
||||
Mailer.send = function(options, fn) {
|
||||
var dataSource = this.dataSource;
|
||||
var settings = dataSource && dataSource.settings;
|
||||
var connector = dataSource.connector;
|
||||
const dataSource = this.dataSource;
|
||||
const settings = dataSource && dataSource.settings;
|
||||
const connector = dataSource.connector;
|
||||
assert(connector, 'Cannot send mail without a connector!');
|
||||
|
||||
var transport = connector.transportForName(options.transport);
|
||||
let transport = connector.transportForName(options.transport);
|
||||
|
||||
if (!transport) {
|
||||
transport = connector.defaultTransport();
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// Copyright IBM Corp. 2013,2014. All Rights Reserved.
|
||||
// Copyright IBM Corp. 2013,2019. All Rights Reserved.
|
||||
// Node module: loopback
|
||||
// This file is licensed under the MIT License.
|
||||
// License text available at https://opensource.org/licenses/MIT
|
||||
|
@ -14,12 +14,12 @@ module.exports = Memory;
|
|||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var Connector = require('./base-connector');
|
||||
var debug = require('debug')('memory');
|
||||
var util = require('util');
|
||||
var inherits = util.inherits;
|
||||
var assert = require('assert');
|
||||
var JdbMemory = require('loopback-datasource-juggler/lib/connectors/memory');
|
||||
const Connector = require('./base-connector');
|
||||
const debug = require('debug')('memory');
|
||||
const util = require('util');
|
||||
const inherits = util.inherits;
|
||||
const assert = require('assert');
|
||||
const JdbMemory = require('loopback-datasource-juggler/lib/connectors/memory');
|
||||
|
||||
/**
|
||||
* Create a new `Memory` connector with the given `options`.
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
// Copyright IBM Corp. 2015,2016. All Rights Reserved.
|
||||
// Copyright IBM Corp. 2016,2019. All Rights Reserved.
|
||||
// Node module: loopback
|
||||
// This file is licensed under the MIT License.
|
||||
// License text available at https://opensource.org/licenses/MIT
|
||||
|
||||
'use strict';
|
||||
var g = require('./globalize');
|
||||
var juggler = require('loopback-datasource-juggler');
|
||||
var remoting = require('strong-remoting');
|
||||
const g = require('./globalize');
|
||||
const juggler = require('loopback-datasource-juggler');
|
||||
const remoting = require('strong-remoting');
|
||||
|
||||
module.exports = function(loopback) {
|
||||
juggler.getCurrentContext =
|
||||
|
@ -15,20 +15,23 @@ module.exports = function(loopback) {
|
|||
throw new Error(g.f(
|
||||
'%s was removed in version 3.0. See %s for more details.',
|
||||
'loopback.getCurrentContext()',
|
||||
'http://loopback.io/doc/en/lb2/Using-current-context.html'));
|
||||
'http://loopback.io/doc/en/lb2/Using-current-context.html',
|
||||
));
|
||||
};
|
||||
|
||||
loopback.runInContext = function(fn) {
|
||||
throw new Error(g.f(
|
||||
'%s was removed in version 3.0. See %s for more details.',
|
||||
'loopback.runInContext()',
|
||||
'http://loopback.io/doc/en/lb2/Using-current-context.html'));
|
||||
'http://loopback.io/doc/en/lb2/Using-current-context.html',
|
||||
));
|
||||
};
|
||||
|
||||
loopback.createContext = function(scopeName) {
|
||||
throw new Error(g.f(
|
||||
'%s was removed in version 3.0. See %s for more details.',
|
||||
'loopback.createContext()',
|
||||
'http://loopback.io/doc/en/lb2/Using-current-context.html'));
|
||||
'http://loopback.io/doc/en/lb2/Using-current-context.html',
|
||||
));
|
||||
};
|
||||
};
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
// Copyright IBM Corp. 2016. All Rights Reserved.
|
||||
// Copyright IBM Corp. 2016,2019. All Rights Reserved.
|
||||
// Node module: loopback
|
||||
// This file is licensed under the MIT License.
|
||||
// License text available at https://opensource.org/licenses/MIT
|
||||
|
||||
'use strict';
|
||||
var path = require('path');
|
||||
var SG = require('strong-globalize');
|
||||
const path = require('path');
|
||||
const SG = require('strong-globalize');
|
||||
|
||||
SG.SetRootDir(path.join(__dirname, '..'), {autonomousMsgLoading: 'all'});
|
||||
module.exports = SG();
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// Copyright IBM Corp. 2013,2016. All Rights Reserved.
|
||||
// Copyright IBM Corp. 2013,2019. All Rights Reserved.
|
||||
// Node module: loopback
|
||||
// This file is licensed under the MIT License.
|
||||
// License text available at https://opensource.org/licenses/MIT
|
||||
|
@ -8,16 +8,17 @@
|
|||
*/
|
||||
|
||||
'use strict';
|
||||
var express = require('express');
|
||||
var loopbackExpress = require('./server-app');
|
||||
var proto = require('./application');
|
||||
var fs = require('fs');
|
||||
var ejs = require('ejs');
|
||||
var path = require('path');
|
||||
var merge = require('util')._extend;
|
||||
var assert = require('assert');
|
||||
var Registry = require('./registry');
|
||||
var juggler = require('loopback-datasource-juggler');
|
||||
const express = require('express');
|
||||
const loopbackExpress = require('./server-app');
|
||||
const proto = require('./application');
|
||||
const fs = require('fs');
|
||||
const ejs = require('ejs');
|
||||
const path = require('path');
|
||||
const merge = require('util')._extend;
|
||||
const assert = require('assert');
|
||||
const Registry = require('./registry');
|
||||
const juggler = require('loopback-datasource-juggler');
|
||||
const configureSharedMethods = require('./configure-shared-methods');
|
||||
|
||||
/**
|
||||
* LoopBack core module. It provides static properties and
|
||||
|
@ -39,7 +40,7 @@ var juggler = require('loopback-datasource-juggler');
|
|||
* @header loopback
|
||||
*/
|
||||
|
||||
var loopback = module.exports = createApplication;
|
||||
const loopback = module.exports = createApplication;
|
||||
|
||||
/*!
|
||||
* Framework version.
|
||||
|
@ -72,12 +73,19 @@ Object.defineProperties(loopback, {
|
|||
*/
|
||||
|
||||
function createApplication(options) {
|
||||
var app = loopbackExpress();
|
||||
const app = loopbackExpress();
|
||||
|
||||
merge(app, proto);
|
||||
|
||||
app.loopback = loopback;
|
||||
|
||||
app.on('modelRemoted', function() {
|
||||
app.models().forEach(function(Model) {
|
||||
if (!Model.config) return;
|
||||
configureSharedMethods(Model, app.get('remoting'), Model.config);
|
||||
});
|
||||
});
|
||||
|
||||
// Create a new instance of models registry per each app instance
|
||||
app.models = function() {
|
||||
return proto.models.apply(this, arguments);
|
||||
|
@ -99,7 +107,7 @@ function createApplication(options) {
|
|||
|
||||
if (loopback.localRegistry || options && options.localRegistry === true) {
|
||||
// setup the app registry
|
||||
var registry = app.registry = new Registry();
|
||||
const registry = app.registry = new Registry();
|
||||
if (options && options.loadBuiltinModels === true) {
|
||||
require('./builtin-models')(registry);
|
||||
}
|
||||
|
@ -111,8 +119,8 @@ function createApplication(options) {
|
|||
}
|
||||
|
||||
function mixin(source) {
|
||||
for (var key in source) {
|
||||
var desc = Object.getOwnPropertyDescriptor(source, key);
|
||||
for (const key in source) {
|
||||
const desc = Object.getOwnPropertyDescriptor(source, key);
|
||||
|
||||
// Fix for legacy (pre-ES5) browsers like PhantomJS
|
||||
if (!desc) continue;
|
||||
|
@ -191,13 +199,13 @@ loopback.remoteMethod = function(fn, options) {
|
|||
* var render = loopback.template('foo.ejs');
|
||||
* var html = render({foo: 'bar'});
|
||||
*
|
||||
* @param {String} path Path to the template file.
|
||||
* @param {String} file Path to the template file.
|
||||
* @returns {Function}
|
||||
*/
|
||||
|
||||
loopback.template = function(file) {
|
||||
var templates = this._templates || (this._templates = {});
|
||||
var str = templates[file] || (templates[file] = fs.readFileSync(file, 'utf8'));
|
||||
const templates = this._templates || (this._templates = {});
|
||||
const str = templates[file] || (templates[file] = fs.readFileSync(file, 'utf8'));
|
||||
return ejs.compile(str, {
|
||||
filename: file,
|
||||
});
|
||||
|
|
346
lib/model.js
346
lib/model.js
|
@ -1,4 +1,4 @@
|
|||
// Copyright IBM Corp. 2014,2016. All Rights Reserved.
|
||||
// Copyright IBM Corp. 2014,2019. All Rights Reserved.
|
||||
// Node module: loopback
|
||||
// This file is licensed under the MIT License.
|
||||
// License text available at https://opensource.org/licenses/MIT
|
||||
|
@ -7,15 +7,14 @@
|
|||
* Module Dependencies.
|
||||
*/
|
||||
'use strict';
|
||||
var g = require('./globalize');
|
||||
var assert = require('assert');
|
||||
var debug = require('debug')('loopback:model');
|
||||
var RemoteObjects = require('strong-remoting');
|
||||
var SharedClass = require('strong-remoting').SharedClass;
|
||||
var extend = require('util')._extend;
|
||||
var format = require('util').format;
|
||||
const g = require('./globalize');
|
||||
const assert = require('assert');
|
||||
const debug = require('debug')('loopback:model');
|
||||
const RemoteObjects = require('strong-remoting');
|
||||
const SharedClass = require('strong-remoting').SharedClass;
|
||||
const extend = require('util')._extend;
|
||||
|
||||
var deprecated = require('depd')('loopback');
|
||||
const deprecated = require('depd')('loopback');
|
||||
|
||||
module.exports = function(registry) {
|
||||
/**
|
||||
|
@ -105,7 +104,7 @@ module.exports = function(registry) {
|
|||
* @property {Array.<Object>} settings.acls Array of ACLs for the model.
|
||||
* @class
|
||||
*/
|
||||
var Model = registry.modelBuilder.define('Model');
|
||||
const Model = registry.modelBuilder.define('Model');
|
||||
|
||||
Model.registry = registry;
|
||||
|
||||
|
@ -116,23 +115,23 @@ module.exports = function(registry) {
|
|||
*/
|
||||
|
||||
Model.setup = function() {
|
||||
var ModelCtor = this;
|
||||
var Parent = this.super_;
|
||||
const ModelCtor = this;
|
||||
const Parent = this.super_;
|
||||
|
||||
if (!ModelCtor.registry && Parent && Parent.registry) {
|
||||
ModelCtor.registry = Parent.registry;
|
||||
}
|
||||
|
||||
var options = this.settings;
|
||||
var typeName = this.modelName;
|
||||
const options = this.settings;
|
||||
const typeName = this.modelName;
|
||||
|
||||
// support remoting prototype methods
|
||||
// it's important to setup this function *before* calling `new SharedClass`
|
||||
// otherwise remoting metadata from our base model is picked up
|
||||
ModelCtor.sharedCtor = function(data, id, options, fn) {
|
||||
var ModelCtor = this;
|
||||
const ModelCtor = this;
|
||||
|
||||
var isRemoteInvocationWithOptions = typeof data !== 'object' &&
|
||||
const isRemoteInvocationWithOptions = typeof data !== 'object' &&
|
||||
typeof id === 'object' &&
|
||||
typeof options === 'function';
|
||||
if (isRemoteInvocationWithOptions) {
|
||||
|
@ -161,14 +160,14 @@ module.exports = function(registry) {
|
|||
}
|
||||
}
|
||||
|
||||
if (id && data) {
|
||||
var model = new ModelCtor(data);
|
||||
if (id != null && data) {
|
||||
const model = new ModelCtor(data);
|
||||
model.id = id;
|
||||
fn(null, model);
|
||||
} else if (data) {
|
||||
fn(null, new ModelCtor(data));
|
||||
} else if (id) {
|
||||
var filter = {};
|
||||
} else if (id != null) {
|
||||
const filter = {};
|
||||
ModelCtor.findById(id, filter, options, function(err, model) {
|
||||
if (err) {
|
||||
fn(err);
|
||||
|
@ -186,7 +185,7 @@ module.exports = function(registry) {
|
|||
}
|
||||
};
|
||||
|
||||
var idDesc = ModelCtor.modelName + ' id';
|
||||
const idDesc = ModelCtor.modelName + ' id';
|
||||
ModelCtor.sharedCtor.accepts = [
|
||||
{arg: 'id', type: 'any', required: true, http: {source: 'path'},
|
||||
description: idDesc},
|
||||
|
@ -200,21 +199,21 @@ module.exports = function(registry) {
|
|||
|
||||
ModelCtor.sharedCtor.returns = {root: true};
|
||||
|
||||
var remotingOptions = {};
|
||||
const remotingOptions = {};
|
||||
extend(remotingOptions, options.remoting || {});
|
||||
|
||||
// create a sharedClass
|
||||
var sharedClass = ModelCtor.sharedClass = new SharedClass(
|
||||
const sharedClass = ModelCtor.sharedClass = new SharedClass(
|
||||
ModelCtor.modelName,
|
||||
ModelCtor,
|
||||
remotingOptions
|
||||
remotingOptions,
|
||||
);
|
||||
|
||||
// before remote hook
|
||||
ModelCtor.beforeRemote = function(name, fn) {
|
||||
var className = this.modelName;
|
||||
const className = this.modelName;
|
||||
this._runWhenAttachedToApp(function(app) {
|
||||
var remotes = app.remotes();
|
||||
const remotes = app.remotes();
|
||||
remotes.before(className + '.' + name, function(ctx, next) {
|
||||
return fn(ctx, ctx.result, next);
|
||||
});
|
||||
|
@ -223,9 +222,9 @@ module.exports = function(registry) {
|
|||
|
||||
// after remote hook
|
||||
ModelCtor.afterRemote = function(name, fn) {
|
||||
var className = this.modelName;
|
||||
const className = this.modelName;
|
||||
this._runWhenAttachedToApp(function(app) {
|
||||
var remotes = app.remotes();
|
||||
const remotes = app.remotes();
|
||||
remotes.after(className + '.' + name, function(ctx, next) {
|
||||
return fn(ctx, ctx.result, next);
|
||||
});
|
||||
|
@ -233,16 +232,16 @@ module.exports = function(registry) {
|
|||
};
|
||||
|
||||
ModelCtor.afterRemoteError = function(name, fn) {
|
||||
var className = this.modelName;
|
||||
const className = this.modelName;
|
||||
this._runWhenAttachedToApp(function(app) {
|
||||
var remotes = app.remotes();
|
||||
const remotes = app.remotes();
|
||||
remotes.afterError(className + '.' + name, fn);
|
||||
});
|
||||
};
|
||||
|
||||
ModelCtor._runWhenAttachedToApp = function(fn) {
|
||||
if (this.app) return fn(this.app);
|
||||
var self = this;
|
||||
const self = this;
|
||||
self.once('attached', function() {
|
||||
fn(self.app);
|
||||
});
|
||||
|
@ -251,17 +250,19 @@ module.exports = function(registry) {
|
|||
if ('injectOptionsFromRemoteContext' in options) {
|
||||
console.warn(g.f(
|
||||
'%s is using model setting %s which is no longer available.',
|
||||
typeName, 'injectOptionsFromRemoteContext'));
|
||||
typeName, 'injectOptionsFromRemoteContext',
|
||||
));
|
||||
console.warn(g.f(
|
||||
'Please rework your app to use the offical solution for injecting ' +
|
||||
'"options" argument from request context,\nsee %s',
|
||||
'http://loopback.io/doc/en/lb3/Using-current-context.html'));
|
||||
'http://loopback.io/doc/en/lb3/Using-current-context.html',
|
||||
));
|
||||
}
|
||||
|
||||
// resolve relation functions
|
||||
sharedClass.resolve(function resolver(define) {
|
||||
var relations = ModelCtor.relations || {};
|
||||
var defineRaw = define;
|
||||
const relations = ModelCtor.relations || {};
|
||||
const defineRaw = define;
|
||||
define = function(name, options, fn) {
|
||||
if (options.accepts) {
|
||||
options = extend({}, options);
|
||||
|
@ -271,8 +272,8 @@ module.exports = function(registry) {
|
|||
};
|
||||
|
||||
// get the relations
|
||||
for (var relationName in relations) {
|
||||
var relation = relations[relationName];
|
||||
for (const relationName in relations) {
|
||||
const relation = relations[relationName];
|
||||
if (relation.type === 'belongsTo') {
|
||||
ModelCtor.belongsToRemoting(relationName, relation, define);
|
||||
} else if (
|
||||
|
@ -286,11 +287,16 @@ module.exports = function(registry) {
|
|||
relation.type === 'referencesMany') {
|
||||
ModelCtor.hasManyRemoting(relationName, relation, define);
|
||||
}
|
||||
// Automatically enable nestRemoting if the flag is set to true in the
|
||||
// relation options
|
||||
if (relation.options && relation.options.nestRemoting) {
|
||||
ModelCtor.nestRemoting(relationName);
|
||||
}
|
||||
}
|
||||
|
||||
// handle scopes
|
||||
var scopes = ModelCtor.scopes || {};
|
||||
for (var scopeName in scopes) {
|
||||
const scopes = ModelCtor.scopes || {};
|
||||
for (const scopeName in scopes) {
|
||||
ModelCtor.scopeRemoting(scopeName, scopes[scopeName], define);
|
||||
}
|
||||
});
|
||||
|
@ -301,9 +307,9 @@ module.exports = function(registry) {
|
|||
/*!
|
||||
* Get the reference to ACL in a lazy fashion to avoid race condition in require
|
||||
*/
|
||||
var _aclModel = null;
|
||||
let _aclModel = null;
|
||||
Model._ACL = function getACL(ACL) {
|
||||
var registry = this.registry;
|
||||
const registry = this.registry;
|
||||
if (ACL !== undefined) {
|
||||
// The function is used as a setter
|
||||
_aclModel = ACL;
|
||||
|
@ -311,7 +317,7 @@ module.exports = function(registry) {
|
|||
if (_aclModel) {
|
||||
return _aclModel;
|
||||
}
|
||||
var aclModel = registry.getModel('ACL');
|
||||
const aclModel = registry.getModel('ACL');
|
||||
_aclModel = registry.getModelByType(aclModel);
|
||||
return _aclModel;
|
||||
};
|
||||
|
@ -329,9 +335,9 @@ module.exports = function(registry) {
|
|||
*/
|
||||
|
||||
Model.checkAccess = function(token, modelId, sharedMethod, ctx, callback) {
|
||||
var ANONYMOUS = registry.getModel('AccessToken').ANONYMOUS;
|
||||
const ANONYMOUS = registry.getModel('AccessToken').ANONYMOUS;
|
||||
token = token || ANONYMOUS;
|
||||
var aclModel = Model._ACL();
|
||||
const aclModel = Model._ACL();
|
||||
|
||||
ctx = ctx || {};
|
||||
if (typeof ctx === 'function' && callback === undefined) {
|
||||
|
@ -367,10 +373,10 @@ module.exports = function(registry) {
|
|||
}
|
||||
assert(
|
||||
typeof method === 'object',
|
||||
'method is a required argument and must be a RemoteMethod object'
|
||||
'method is a required argument and must be a RemoteMethod object',
|
||||
);
|
||||
|
||||
var ACL = Model._ACL();
|
||||
const ACL = Model._ACL();
|
||||
|
||||
// Check the explicit setting of accessType
|
||||
if (method.accessType) {
|
||||
|
@ -384,7 +390,7 @@ module.exports = function(registry) {
|
|||
}
|
||||
|
||||
// Default GET requests to READ
|
||||
var verb = method.http && method.http.verb;
|
||||
let verb = method.http && method.http.verb;
|
||||
if (typeof verb === 'string') {
|
||||
verb = verb.toUpperCase();
|
||||
}
|
||||
|
@ -432,7 +438,7 @@ module.exports = function(registry) {
|
|||
*/
|
||||
|
||||
Model.getApp = function(callback) {
|
||||
var self = this;
|
||||
const self = this;
|
||||
self._runWhenAttachedToApp(function(app) {
|
||||
assert(self.app);
|
||||
assert.equal(app, self.app);
|
||||
|
@ -457,21 +463,21 @@ module.exports = function(registry) {
|
|||
|
||||
Model.remoteMethod = function(name, options) {
|
||||
if (options.isStatic === undefined) {
|
||||
var m = name.match(/^prototype\.(.*)$/);
|
||||
const m = name.match(/^prototype\.(.*)$/);
|
||||
options.isStatic = !m;
|
||||
name = options.isStatic ? name : m[1];
|
||||
}
|
||||
|
||||
if (options.accepts) {
|
||||
options = extend({}, options);
|
||||
options.accepts = setupOptionsArgs(options.accepts);
|
||||
options.accepts = setupOptionsArgs(options.accepts, this);
|
||||
}
|
||||
|
||||
this.sharedClass.defineMethod(name, options);
|
||||
this.emit('remoteMethodAdded', this.sharedClass);
|
||||
};
|
||||
|
||||
function setupOptionsArgs(accepts) {
|
||||
function setupOptionsArgs(accepts, modelClass) {
|
||||
if (!Array.isArray(accepts))
|
||||
accepts = [accepts];
|
||||
|
||||
|
@ -479,21 +485,33 @@ module.exports = function(registry) {
|
|||
if (arg.http && arg.http === 'optionsFromRequest') {
|
||||
// clone to preserve the input value
|
||||
arg = extend({}, arg);
|
||||
arg.http = createOptionsViaModelMethod;
|
||||
arg.http = createOptionsViaModelMethod.bind(modelClass);
|
||||
}
|
||||
return arg;
|
||||
});
|
||||
}
|
||||
|
||||
function createOptionsViaModelMethod(ctx) {
|
||||
var EMPTY_OPTIONS = {};
|
||||
var ModelCtor = ctx.method && ctx.method.ctor;
|
||||
if (!ModelCtor)
|
||||
return EMPTY_OPTIONS;
|
||||
const ModelCtor = (ctx.method && ctx.method.ctor) || this;
|
||||
|
||||
/**
|
||||
* Configure default values for juggler settings to protect user-supplied
|
||||
* input from attacking juggler
|
||||
*/
|
||||
const DEFAULT_OPTIONS = {
|
||||
// Default to `true` so that hidden properties cannot be used in query
|
||||
prohibitHiddenPropertiesInQuery: ModelCtor._getProhibitHiddenPropertiesInQuery({}, true),
|
||||
// Default to `12` for the max depth of a query object
|
||||
maxDepthOfQuery: ModelCtor._getMaxDepthOfQuery({}, 12),
|
||||
// Default to `32` for the max depth of a data object
|
||||
maxDepthOfData: ModelCtor._getMaxDepthOfData({}, 32),
|
||||
};
|
||||
|
||||
if (typeof ModelCtor.createOptionsFromRemotingContext !== 'function')
|
||||
return EMPTY_OPTIONS;
|
||||
return DEFAULT_OPTIONS;
|
||||
debug('createOptionsFromRemotingContext for %s', ctx.method.stringName);
|
||||
return ModelCtor.createOptionsFromRemotingContext(ctx);
|
||||
return Object.assign(DEFAULT_OPTIONS,
|
||||
ModelCtor.createOptionsFromRemotingContext(ctx));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -508,7 +526,7 @@ module.exports = function(registry) {
|
|||
Model.disableRemoteMethod = function(name, isStatic) {
|
||||
deprecated('Model.disableRemoteMethod is deprecated. ' +
|
||||
'Use Model.disableRemoteMethodByName instead.');
|
||||
var key = this.sharedClass.getKeyFromMethodNameAndTarget(name, isStatic);
|
||||
const key = this.sharedClass.getKeyFromMethodNameAndTarget(name, isStatic);
|
||||
this.sharedClass.disableMethodByName(key);
|
||||
this.emit('remoteMethodDisabled', this.sharedClass, key);
|
||||
};
|
||||
|
@ -526,10 +544,10 @@ module.exports = function(registry) {
|
|||
};
|
||||
|
||||
Model.belongsToRemoting = function(relationName, relation, define) {
|
||||
var modelName = relation.modelTo && relation.modelTo.modelName;
|
||||
let modelName = relation.modelTo && relation.modelTo.modelName;
|
||||
modelName = modelName || 'PersistedModel';
|
||||
var fn = this.prototype[relationName];
|
||||
var pathName = (relation.options.http && relation.options.http.path) || relationName;
|
||||
const fn = this.prototype[relationName];
|
||||
const pathName = (relation.options.http && relation.options.http.path) || relationName;
|
||||
define('__get__' + relationName, {
|
||||
isStatic: false,
|
||||
http: {verb: 'get', path: '/' + pathName},
|
||||
|
@ -538,7 +556,7 @@ module.exports = function(registry) {
|
|||
{arg: 'options', type: 'object', http: 'optionsFromRequest'},
|
||||
],
|
||||
accessType: 'READ',
|
||||
description: format('Fetches belongsTo relation %s.', relationName),
|
||||
description: g.f('Fetches belongsTo relation %s.', relationName),
|
||||
returns: {arg: relationName, type: modelName, root: true},
|
||||
}, fn);
|
||||
};
|
||||
|
@ -546,17 +564,19 @@ module.exports = function(registry) {
|
|||
function convertNullToNotFoundError(toModelName, ctx, cb) {
|
||||
if (ctx.result !== null) return cb();
|
||||
|
||||
var fk = ctx.getArgByName('fk');
|
||||
var msg = g.f('Unknown "%s" id "%s".', toModelName, fk);
|
||||
var error = new Error(msg);
|
||||
const fk = ctx.getArgByName('fk');
|
||||
const msg = fk ?
|
||||
g.f('Unknown "%s" id "%s".', toModelName, fk) :
|
||||
g.f('No "%s" instance(s) found', toModelName);
|
||||
const error = new Error(msg);
|
||||
error.statusCode = error.status = 404;
|
||||
error.code = 'MODEL_NOT_FOUND';
|
||||
cb(error);
|
||||
}
|
||||
|
||||
Model.hasOneRemoting = function(relationName, relation, define) {
|
||||
var pathName = (relation.options.http && relation.options.http.path) || relationName;
|
||||
var toModelName = relation.modelTo.modelName;
|
||||
const pathName = (relation.options.http && relation.options.http.path) || relationName;
|
||||
const toModelName = relation.modelTo.modelName;
|
||||
|
||||
define('__get__' + relationName, {
|
||||
isStatic: false,
|
||||
|
@ -565,7 +585,7 @@ module.exports = function(registry) {
|
|||
{arg: 'refresh', type: 'boolean', http: {source: 'query'}},
|
||||
{arg: 'options', type: 'object', http: 'optionsFromRequest'},
|
||||
],
|
||||
description: format('Fetches hasOne relation %s.', relationName),
|
||||
description: g.f('Fetches hasOne relation %s.', relationName),
|
||||
accessType: 'READ',
|
||||
returns: {arg: relationName, type: relation.modelTo.modelName, root: true},
|
||||
rest: {after: convertNullToNotFoundError.bind(null, toModelName)},
|
||||
|
@ -581,7 +601,7 @@ module.exports = function(registry) {
|
|||
},
|
||||
{arg: 'options', type: 'object', http: 'optionsFromRequest'},
|
||||
],
|
||||
description: format('Creates a new instance in %s of this model.', relationName),
|
||||
description: g.f('Creates a new instance in %s of this model.', relationName),
|
||||
accessType: 'WRITE',
|
||||
returns: {arg: 'data', type: toModelName, root: true},
|
||||
});
|
||||
|
@ -596,7 +616,7 @@ module.exports = function(registry) {
|
|||
},
|
||||
{arg: 'options', type: 'object', http: 'optionsFromRequest'},
|
||||
],
|
||||
description: format('Update %s of this model.', relationName),
|
||||
description: g.f('Update %s of this model.', relationName),
|
||||
accessType: 'WRITE',
|
||||
returns: {arg: 'data', type: toModelName, root: true},
|
||||
});
|
||||
|
@ -607,73 +627,73 @@ module.exports = function(registry) {
|
|||
accepts: [
|
||||
{arg: 'options', type: 'object', http: 'optionsFromRequest'},
|
||||
],
|
||||
description: format('Deletes %s of this model.', relationName),
|
||||
description: g.f('Deletes %s of this model.', relationName),
|
||||
accessType: 'WRITE',
|
||||
});
|
||||
};
|
||||
|
||||
Model.hasManyRemoting = function(relationName, relation, define) {
|
||||
var pathName = (relation.options.http && relation.options.http.path) || relationName;
|
||||
var toModelName = relation.modelTo.modelName;
|
||||
const pathName = (relation.options.http && relation.options.http.path) || relationName;
|
||||
const toModelName = relation.modelTo.modelName;
|
||||
|
||||
var findByIdFunc = this.prototype['__findById__' + relationName];
|
||||
const findByIdFunc = this.prototype['__findById__' + relationName];
|
||||
define('__findById__' + relationName, {
|
||||
isStatic: false,
|
||||
http: {verb: 'get', path: '/' + pathName + '/:fk'},
|
||||
accepts: [
|
||||
{
|
||||
arg: 'fk', type: 'any',
|
||||
description: format('Foreign key for %s', relationName),
|
||||
description: g.f('Foreign key for %s', relationName),
|
||||
required: true,
|
||||
http: {source: 'path'},
|
||||
},
|
||||
{arg: 'options', type: 'object', http: 'optionsFromRequest'},
|
||||
],
|
||||
description: format('Find a related item by id for %s.', relationName),
|
||||
description: g.f('Find a related item by id for %s.', relationName),
|
||||
accessType: 'READ',
|
||||
returns: {arg: 'result', type: toModelName, root: true},
|
||||
rest: {after: convertNullToNotFoundError.bind(null, toModelName)},
|
||||
}, findByIdFunc);
|
||||
|
||||
var destroyByIdFunc = this.prototype['__destroyById__' + relationName];
|
||||
const destroyByIdFunc = this.prototype['__destroyById__' + relationName];
|
||||
define('__destroyById__' + relationName, {
|
||||
isStatic: false,
|
||||
http: {verb: 'delete', path: '/' + pathName + '/:fk'},
|
||||
accepts: [
|
||||
{
|
||||
arg: 'fk', type: 'any',
|
||||
description: format('Foreign key for %s', relationName),
|
||||
description: g.f('Foreign key for %s', relationName),
|
||||
required: true,
|
||||
http: {source: 'path'},
|
||||
},
|
||||
{arg: 'options', type: 'object', http: 'optionsFromRequest'},
|
||||
],
|
||||
description: format('Delete a related item by id for %s.', relationName),
|
||||
description: g.f('Delete a related item by id for %s.', relationName),
|
||||
accessType: 'WRITE',
|
||||
returns: [],
|
||||
}, destroyByIdFunc);
|
||||
|
||||
var updateByIdFunc = this.prototype['__updateById__' + relationName];
|
||||
const updateByIdFunc = this.prototype['__updateById__' + relationName];
|
||||
define('__updateById__' + relationName, {
|
||||
isStatic: false,
|
||||
http: {verb: 'put', path: '/' + pathName + '/:fk'},
|
||||
accepts: [
|
||||
{arg: 'fk', type: 'any',
|
||||
description: format('Foreign key for %s', relationName),
|
||||
description: g.f('Foreign key for %s', relationName),
|
||||
required: true,
|
||||
http: {source: 'path'}},
|
||||
{arg: 'data', type: 'object', model: toModelName, http: {source: 'body'}},
|
||||
{arg: 'options', type: 'object', http: 'optionsFromRequest'},
|
||||
],
|
||||
description: format('Update a related item by id for %s.', relationName),
|
||||
description: g.f('Update a related item by id for %s.', relationName),
|
||||
accessType: 'WRITE',
|
||||
returns: {arg: 'result', type: toModelName, root: true},
|
||||
}, updateByIdFunc);
|
||||
|
||||
if (relation.modelThrough || relation.type === 'referencesMany') {
|
||||
var modelThrough = relation.modelThrough || relation.modelTo;
|
||||
const modelThrough = relation.modelThrough || relation.modelTo;
|
||||
|
||||
var accepts = [];
|
||||
const accepts = [];
|
||||
if (relation.type === 'hasMany' && relation.modelThrough) {
|
||||
// Restrict: only hasManyThrough relation can have additional properties
|
||||
accepts.push({
|
||||
|
@ -682,66 +702,66 @@ module.exports = function(registry) {
|
|||
});
|
||||
}
|
||||
|
||||
var addFunc = this.prototype['__link__' + relationName];
|
||||
const addFunc = this.prototype['__link__' + relationName];
|
||||
define('__link__' + relationName, {
|
||||
isStatic: false,
|
||||
http: {verb: 'put', path: '/' + pathName + '/rel/:fk'},
|
||||
accepts: [{arg: 'fk', type: 'any',
|
||||
description: format('Foreign key for %s', relationName),
|
||||
description: g.f('Foreign key for %s', relationName),
|
||||
required: true,
|
||||
http: {source: 'path'}},
|
||||
].concat(accepts).concat([
|
||||
{arg: 'options', type: 'object', http: 'optionsFromRequest'},
|
||||
]),
|
||||
description: format('Add a related item by id for %s.', relationName),
|
||||
description: g.f('Add a related item by id for %s.', relationName),
|
||||
accessType: 'WRITE',
|
||||
returns: {arg: relationName, type: modelThrough.modelName, root: true},
|
||||
}, addFunc);
|
||||
|
||||
var removeFunc = this.prototype['__unlink__' + relationName];
|
||||
const removeFunc = this.prototype['__unlink__' + relationName];
|
||||
define('__unlink__' + relationName, {
|
||||
isStatic: false,
|
||||
http: {verb: 'delete', path: '/' + pathName + '/rel/:fk'},
|
||||
accepts: [
|
||||
{
|
||||
arg: 'fk', type: 'any',
|
||||
description: format('Foreign key for %s', relationName),
|
||||
description: g.f('Foreign key for %s', relationName),
|
||||
required: true,
|
||||
http: {source: 'path'},
|
||||
},
|
||||
{arg: 'options', type: 'object', http: 'optionsFromRequest'},
|
||||
],
|
||||
description: format('Remove the %s relation to an item by id.', relationName),
|
||||
description: g.f('Remove the %s relation to an item by id.', relationName),
|
||||
accessType: 'WRITE',
|
||||
returns: [],
|
||||
}, removeFunc);
|
||||
|
||||
// FIXME: [rfeng] How to map a function with callback(err, true|false) to HEAD?
|
||||
// true --> 200 and false --> 404?
|
||||
var existsFunc = this.prototype['__exists__' + relationName];
|
||||
const existsFunc = this.prototype['__exists__' + relationName];
|
||||
define('__exists__' + relationName, {
|
||||
isStatic: false,
|
||||
http: {verb: 'head', path: '/' + pathName + '/rel/:fk'},
|
||||
accepts: [
|
||||
{
|
||||
arg: 'fk', type: 'any',
|
||||
description: format('Foreign key for %s', relationName),
|
||||
description: g.f('Foreign key for %s', relationName),
|
||||
required: true,
|
||||
http: {source: 'path'},
|
||||
},
|
||||
{arg: 'options', type: 'object', http: 'optionsFromRequest'},
|
||||
],
|
||||
description: format('Check the existence of %s relation to an item by id.', relationName),
|
||||
description: g.f('Check the existence of %s relation to an item by id.', relationName),
|
||||
accessType: 'READ',
|
||||
returns: {arg: 'exists', type: 'boolean', root: true},
|
||||
rest: {
|
||||
// After hook to map exists to 200/404 for HEAD
|
||||
after: function(ctx, cb) {
|
||||
if (ctx.result === false) {
|
||||
var modelName = ctx.method.sharedClass.name;
|
||||
var id = ctx.getArgByName('id');
|
||||
var msg = g.f('Unknown "%s" {{id}} "%s".', modelName, id);
|
||||
var error = new Error(msg);
|
||||
const modelName = ctx.method.sharedClass.name;
|
||||
const id = ctx.getArgByName('id');
|
||||
const msg = g.f('Unknown "%s" {{id}} "%s".', modelName, id);
|
||||
const error = new Error(msg);
|
||||
error.statusCode = error.status = 404;
|
||||
error.code = 'MODEL_NOT_FOUND';
|
||||
cb(error);
|
||||
|
@ -755,27 +775,31 @@ module.exports = function(registry) {
|
|||
};
|
||||
|
||||
Model.scopeRemoting = function(scopeName, scope, define) {
|
||||
var pathName =
|
||||
const pathName =
|
||||
(scope.options && scope.options.http && scope.options.http.path) || scopeName;
|
||||
|
||||
// if there is atleast one updateOnly property, then we set
|
||||
// createOnlyInstance flag in __create__ to indicate loopback-swagger
|
||||
// code to create a separate model instance for create operation only
|
||||
const updateOnlyProps = this.getUpdateOnlyProperties();
|
||||
const hasUpdateOnlyProps = updateOnlyProps && updateOnlyProps.length > 0;
|
||||
let modelTo = scope.modelTo;
|
||||
|
||||
var isStatic = scope.isStatic;
|
||||
var toModelName = scope.modelTo.modelName;
|
||||
const isStatic = scope.isStatic;
|
||||
let toModelName = scope.modelTo.modelName;
|
||||
|
||||
// https://github.com/strongloop/loopback/issues/811
|
||||
// Check if the scope is for a hasMany relation
|
||||
var relation = this.relations[scopeName];
|
||||
const relation = this.relations[scopeName];
|
||||
if (relation && relation.modelTo) {
|
||||
// For a relation with through model, the toModelName should be the one
|
||||
// from the target model
|
||||
toModelName = relation.modelTo.modelName;
|
||||
modelTo = relation.modelTo;
|
||||
}
|
||||
|
||||
// if there is atleast one updateOnly property, then we set
|
||||
// createOnlyInstance flag in __create__ to indicate loopback-swagger
|
||||
// code to create a separate model instance for create operation only
|
||||
const updateOnlyProps = modelTo.getUpdateOnlyProperties ?
|
||||
modelTo.getUpdateOnlyProperties() : false;
|
||||
const hasUpdateOnlyProps = updateOnlyProps && updateOnlyProps.length > 0;
|
||||
|
||||
define('__get__' + scopeName, {
|
||||
isStatic: isStatic,
|
||||
http: {verb: 'get', path: '/' + pathName},
|
||||
|
@ -783,7 +807,7 @@ module.exports = function(registry) {
|
|||
{arg: 'filter', type: 'object'},
|
||||
{arg: 'options', type: 'object', http: 'optionsFromRequest'},
|
||||
],
|
||||
description: format('Queries %s of %s.', scopeName, this.modelName),
|
||||
description: g.f('Queries %s of %s.', scopeName, this.modelName),
|
||||
accessType: 'READ',
|
||||
returns: {arg: scopeName, type: [toModelName], root: true},
|
||||
});
|
||||
|
@ -802,7 +826,7 @@ module.exports = function(registry) {
|
|||
},
|
||||
{arg: 'options', type: 'object', http: 'optionsFromRequest'},
|
||||
],
|
||||
description: format('Creates a new instance in %s of this model.', scopeName),
|
||||
description: g.f('Creates a new instance in %s of this model.', scopeName),
|
||||
accessType: 'WRITE',
|
||||
returns: {arg: 'data', type: toModelName, root: true},
|
||||
});
|
||||
|
@ -820,7 +844,7 @@ module.exports = function(registry) {
|
|||
},
|
||||
{arg: 'options', type: 'object', http: 'optionsFromRequest'},
|
||||
],
|
||||
description: format('Deletes all %s of this model.', scopeName),
|
||||
description: g.f('Deletes all %s of this model.', scopeName),
|
||||
accessType: 'WRITE',
|
||||
});
|
||||
|
||||
|
@ -834,9 +858,9 @@ module.exports = function(registry) {
|
|||
},
|
||||
{arg: 'options', type: 'object', http: 'optionsFromRequest'},
|
||||
],
|
||||
description: format('Counts %s of %s.', scopeName, this.modelName),
|
||||
description: g.f('Counts %s of %s.', scopeName, this.modelName),
|
||||
accessType: 'READ',
|
||||
returns: {arg: 'count', type: 'number'},
|
||||
returns: {arg: 'count', type: 'number'},
|
||||
});
|
||||
};
|
||||
|
||||
|
@ -862,26 +886,29 @@ module.exports = function(registry) {
|
|||
}
|
||||
options = options || {};
|
||||
|
||||
var regExp = /^__([^_]+)__([^_]+)$/;
|
||||
var relation = this.relations[relationName];
|
||||
if (relation && relation.modelTo && relation.modelTo.sharedClass) {
|
||||
var self = this;
|
||||
var sharedClass = this.sharedClass;
|
||||
var sharedToClass = relation.modelTo.sharedClass;
|
||||
var toModelName = relation.modelTo.modelName;
|
||||
const regExp = /^__([^_]+)__([^_]+)$/;
|
||||
const relation = this.relations[relationName];
|
||||
if (relation && relation._nestRemotingProcessed) {
|
||||
return; // Prevent unwanted circular traversals!
|
||||
} else if (relation && relation.modelTo && relation.modelTo.sharedClass) {
|
||||
relation._nestRemotingProcessed = true;
|
||||
const self = this;
|
||||
const sharedClass = this.sharedClass;
|
||||
const sharedToClass = relation.modelTo.sharedClass;
|
||||
const toModelName = relation.modelTo.modelName;
|
||||
|
||||
var pathName = options.pathName || relation.options.path || relationName;
|
||||
var paramName = options.paramName || 'nk';
|
||||
const pathName = options.pathName || relation.options.path || relationName;
|
||||
const paramName = options.paramName || 'nk';
|
||||
|
||||
var http = [].concat(sharedToClass.http || [])[0];
|
||||
var httpPath, acceptArgs;
|
||||
const http = [].concat(sharedToClass.http || [])[0];
|
||||
let httpPath, acceptArgs;
|
||||
|
||||
if (relation.multiple) {
|
||||
httpPath = pathName + '/:' + paramName;
|
||||
acceptArgs = [
|
||||
{
|
||||
arg: paramName, type: 'any', http: {source: 'path'},
|
||||
description: format('Foreign key for %s.', relation.name),
|
||||
description: g.f('Foreign key for %s.', relation.name),
|
||||
required: true,
|
||||
},
|
||||
];
|
||||
|
@ -896,30 +923,30 @@ module.exports = function(registry) {
|
|||
|
||||
// A method should return the method name to use, if it is to be
|
||||
// included as a nested method - a falsy return value will skip.
|
||||
var filter = filterCallback || options.filterMethod || function(method, relation) {
|
||||
var matches = method.name.match(regExp);
|
||||
const filter = filterCallback || options.filterMethod || function(method, relation) {
|
||||
const matches = method.name.match(regExp);
|
||||
if (matches) {
|
||||
return '__' + matches[1] + '__' + relation.name + '__' + matches[2];
|
||||
}
|
||||
};
|
||||
|
||||
sharedToClass.methods().forEach(function(method) {
|
||||
var methodName;
|
||||
let methodName;
|
||||
if (!method.isStatic && (methodName = filter(method, relation))) {
|
||||
var prefix = relation.multiple ? '__findById__' : '__get__';
|
||||
var getterName = options.getterName || (prefix + relationName);
|
||||
const prefix = relation.multiple ? '__findById__' : '__get__';
|
||||
const getterName = options.getterName || (prefix + relationName);
|
||||
|
||||
var getterFn = relation.modelFrom.prototype[getterName];
|
||||
const getterFn = relation.modelFrom.prototype[getterName];
|
||||
if (typeof getterFn !== 'function') {
|
||||
throw new Error(g.f('Invalid remote method: `%s`', getterName));
|
||||
}
|
||||
|
||||
var nestedFn = relation.modelTo.prototype[method.name];
|
||||
const nestedFn = relation.modelTo.prototype[method.name];
|
||||
if (typeof nestedFn !== 'function') {
|
||||
throw new Error(g.f('Invalid remote method: `%s`', method.name));
|
||||
}
|
||||
|
||||
var opts = {};
|
||||
const opts = {};
|
||||
|
||||
opts.accepts = acceptArgs.concat(method.accepts || []);
|
||||
opts.returns = [].concat(method.returns || []);
|
||||
|
@ -929,47 +956,54 @@ module.exports = function(registry) {
|
|||
opts.rest.delegateTo = method;
|
||||
|
||||
opts.http = [];
|
||||
var routes = [].concat(method.http || []);
|
||||
const routes = [].concat(method.http || []);
|
||||
routes.forEach(function(route) {
|
||||
if (route.path) {
|
||||
var copy = extend({}, route);
|
||||
const copy = extend({}, route);
|
||||
copy.path = httpPath + route.path;
|
||||
opts.http.push(copy);
|
||||
}
|
||||
});
|
||||
|
||||
const lastArg = opts.accepts[opts.accepts.length - 1] || {};
|
||||
const hasOptionsFromContext =
|
||||
(lastArg.arg || lastArg.name) === 'options' &&
|
||||
lastArg.type === 'object' && lastArg.http;
|
||||
|
||||
if (relation.multiple) {
|
||||
sharedClass.defineMethod(methodName, opts, function(fkId) {
|
||||
var args = Array.prototype.slice.call(arguments, 1);
|
||||
var last = args[args.length - 1];
|
||||
var cb = typeof last === 'function' ? last : null;
|
||||
this[getterName](fkId, function(err, inst) {
|
||||
if (err && cb) return cb(err);
|
||||
const args = Array.prototype.slice.call(arguments, 1);
|
||||
const cb = args[args.length - 1];
|
||||
const contextOptions =
|
||||
hasOptionsFromContext && args[args.length - 2] || {};
|
||||
this[getterName](fkId, contextOptions, function(err, inst) {
|
||||
if (err) return cb(err);
|
||||
if (inst instanceof relation.modelTo) {
|
||||
try {
|
||||
nestedFn.apply(inst, args);
|
||||
} catch (err) {
|
||||
if (cb) return cb(err);
|
||||
return cb(err);
|
||||
}
|
||||
} else if (cb) {
|
||||
} else {
|
||||
cb(err, null);
|
||||
}
|
||||
});
|
||||
}, method.isStatic);
|
||||
} else {
|
||||
sharedClass.defineMethod(methodName, opts, function() {
|
||||
var args = Array.prototype.slice.call(arguments);
|
||||
var last = args[args.length - 1];
|
||||
var cb = typeof last === 'function' ? last : null;
|
||||
this[getterName](function(err, inst) {
|
||||
if (err && cb) return cb(err);
|
||||
const args = Array.prototype.slice.call(arguments);
|
||||
const cb = args[args.length - 1];
|
||||
const contextOptions =
|
||||
hasOptionsFromContext && args[args.length - 2] || {};
|
||||
this[getterName](contextOptions, function(err, inst) {
|
||||
if (err) return cb(err);
|
||||
if (inst instanceof relation.modelTo) {
|
||||
try {
|
||||
nestedFn.apply(inst, args);
|
||||
} catch (err) {
|
||||
if (cb) return cb(err);
|
||||
return cb(err);
|
||||
}
|
||||
} else if (cb) {
|
||||
} else {
|
||||
cb(err, null);
|
||||
}
|
||||
});
|
||||
|
@ -983,19 +1017,19 @@ module.exports = function(registry) {
|
|||
if (options.hooks === false) return; // don't inherit before/after hooks
|
||||
|
||||
self.once('mounted', function(app, sc, remotes) {
|
||||
var listenerTree = extend({}, remotes.listenerTree || {});
|
||||
const listenerTree = extend({}, remotes.listenerTree || {});
|
||||
listenerTree.before = listenerTree.before || {};
|
||||
listenerTree.after = listenerTree.after || {};
|
||||
|
||||
var beforeListeners = listenerTree.before[toModelName] || {};
|
||||
var afterListeners = listenerTree.after[toModelName] || {};
|
||||
const beforeListeners = listenerTree.before[toModelName] || {};
|
||||
const afterListeners = listenerTree.after[toModelName] || {};
|
||||
|
||||
sharedClass.methods().forEach(function(method) {
|
||||
var delegateTo = method.rest && method.rest.delegateTo;
|
||||
const delegateTo = method.rest && method.rest.delegateTo;
|
||||
if (delegateTo && delegateTo.ctor == relation.modelTo) {
|
||||
var before = method.isStatic ? beforeListeners : beforeListeners['prototype'];
|
||||
var after = method.isStatic ? afterListeners : afterListeners['prototype'];
|
||||
var m = method.isStatic ? method.name : 'prototype.' + method.name;
|
||||
const before = method.isStatic ? beforeListeners : beforeListeners['prototype'];
|
||||
const after = method.isStatic ? afterListeners : afterListeners['prototype'];
|
||||
const m = method.isStatic ? method.name : 'prototype.' + method.name;
|
||||
if (before && before[delegateTo.name]) {
|
||||
self.beforeRemote(m, function(ctx, result, next) {
|
||||
before[delegateTo.name]._listeners.call(null, ctx, next);
|
||||
|
@ -1010,7 +1044,7 @@ module.exports = function(registry) {
|
|||
});
|
||||
});
|
||||
} else {
|
||||
var msg = g.f('Relation `%s` does not exist for model `%s`', relationName, this.modelName);
|
||||
const msg = g.f('Relation `%s` does not exist for model `%s`', relationName, this.modelName);
|
||||
throw new Error(msg);
|
||||
}
|
||||
};
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// Copyright IBM Corp. 2014,2016. All Rights Reserved.
|
||||
// Copyright IBM Corp. 2014,2019. All Rights Reserved.
|
||||
// Node module: loopback
|
||||
// This file is licensed under the MIT License.
|
||||
// License text available at https://opensource.org/licenses/MIT
|
||||
|
@ -7,18 +7,20 @@
|
|||
* Module Dependencies.
|
||||
*/
|
||||
'use strict';
|
||||
var g = require('./globalize');
|
||||
var runtime = require('./runtime');
|
||||
var assert = require('assert');
|
||||
var async = require('async');
|
||||
var deprecated = require('depd')('loopback');
|
||||
var debug = require('debug')('loopback:persisted-model');
|
||||
var PassThrough = require('stream').PassThrough;
|
||||
var utils = require('./utils');
|
||||
var REPLICATION_CHUNK_SIZE = -1;
|
||||
const g = require('./globalize');
|
||||
const runtime = require('./runtime');
|
||||
const assert = require('assert');
|
||||
const async = require('async');
|
||||
const deprecated = require('depd')('loopback');
|
||||
const debug = require('debug')('loopback:persisted-model');
|
||||
const PassThrough = require('stream').PassThrough;
|
||||
const utils = require('./utils');
|
||||
const filterNodes = require('loopback-filters');
|
||||
|
||||
const REPLICATION_CHUNK_SIZE = -1;
|
||||
|
||||
module.exports = function(registry) {
|
||||
var Model = registry.getModel('Model');
|
||||
const Model = registry.getModel('Model');
|
||||
|
||||
/**
|
||||
* Extends Model with basic query and CRUD support.
|
||||
|
@ -36,7 +38,7 @@ module.exports = function(registry) {
|
|||
* @class PersistedModel
|
||||
*/
|
||||
|
||||
var PersistedModel = Model.extend('PersistedModel');
|
||||
const PersistedModel = Model.extend('PersistedModel');
|
||||
|
||||
/*!
|
||||
* Setup the `PersistedModel` constructor.
|
||||
|
@ -46,7 +48,7 @@ module.exports = function(registry) {
|
|||
// call Model.setup first
|
||||
Model.setup.call(this);
|
||||
|
||||
var PersistedModel = this;
|
||||
const PersistedModel = this;
|
||||
|
||||
// enable change tracking (usually for replication)
|
||||
if (this.settings.trackChanges) {
|
||||
|
@ -70,7 +72,7 @@ module.exports = function(registry) {
|
|||
g.f('Cannot call %s.%s().' +
|
||||
' The %s method has not been setup.' +
|
||||
' The {{PersistedModel}} has not been correctly attached to a {{DataSource}}!',
|
||||
modelName, methodName, methodName)
|
||||
modelName, methodName, methodName),
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -83,10 +85,10 @@ module.exports = function(registry) {
|
|||
function convertNullToNotFoundError(ctx, cb) {
|
||||
if (ctx.result !== null) return cb();
|
||||
|
||||
var modelName = ctx.method.sharedClass.name;
|
||||
var id = ctx.getArgByName('id');
|
||||
var msg = g.f('Unknown "%s" {{id}} "%s".', modelName, id);
|
||||
var error = new Error(msg);
|
||||
const modelName = ctx.method.sharedClass.name;
|
||||
const id = ctx.getArgByName('id');
|
||||
const msg = g.f('Unknown "%s" {{id}} "%s".', modelName, id);
|
||||
const error = new Error(msg);
|
||||
error.statusCode = error.status = 404;
|
||||
error.code = 'MODEL_NOT_FOUND';
|
||||
cb(error);
|
||||
|
@ -401,7 +403,7 @@ module.exports = function(registry) {
|
|||
*/
|
||||
|
||||
PersistedModel.prototype.save = function(options, callback) {
|
||||
var Model = this.constructor;
|
||||
const Model = this.constructor;
|
||||
|
||||
if (typeof options == 'function') {
|
||||
callback = options;
|
||||
|
@ -419,9 +421,9 @@ module.exports = function(registry) {
|
|||
options.throws = false;
|
||||
}
|
||||
|
||||
var inst = this;
|
||||
var data = inst.toObject(true);
|
||||
var id = this.getId();
|
||||
const inst = this;
|
||||
const data = inst.toObject(true);
|
||||
const id = this.getId();
|
||||
|
||||
if (!id) {
|
||||
return Model.create(this, callback);
|
||||
|
@ -436,7 +438,7 @@ module.exports = function(registry) {
|
|||
if (valid) {
|
||||
save();
|
||||
} else {
|
||||
var err = new Model.ValidationError(inst);
|
||||
const err = new Model.ValidationError(inst);
|
||||
// throws option is dangerous for async usage
|
||||
if (options.throws) {
|
||||
throw err;
|
||||
|
@ -579,7 +581,7 @@ module.exports = function(registry) {
|
|||
*/
|
||||
|
||||
PersistedModel.prototype.setId = function(val) {
|
||||
var ds = this.getDataSource();
|
||||
const ds = this.getDataSource();
|
||||
this[this.getIdName()] = val;
|
||||
};
|
||||
|
||||
|
@ -590,7 +592,7 @@ module.exports = function(registry) {
|
|||
*/
|
||||
|
||||
PersistedModel.prototype.getId = function() {
|
||||
var data = this.toObject();
|
||||
const data = this.toObject();
|
||||
if (!data) return;
|
||||
return data[this.getIdName()];
|
||||
};
|
||||
|
@ -612,8 +614,8 @@ module.exports = function(registry) {
|
|||
*/
|
||||
|
||||
PersistedModel.getIdName = function() {
|
||||
var Model = this;
|
||||
var ds = Model.getDataSource();
|
||||
const Model = this;
|
||||
const ds = Model.getDataSource();
|
||||
|
||||
if (ds.idName) {
|
||||
return ds.idName(Model.modelName);
|
||||
|
@ -623,9 +625,9 @@ module.exports = function(registry) {
|
|||
};
|
||||
|
||||
PersistedModel.setupRemoting = function() {
|
||||
var PersistedModel = this;
|
||||
var typeName = PersistedModel.modelName;
|
||||
var options = PersistedModel.settings;
|
||||
const PersistedModel = this;
|
||||
const typeName = PersistedModel.modelName;
|
||||
const options = PersistedModel.settings;
|
||||
|
||||
// if there is atleast one updateOnly property, then we set
|
||||
// createOnlyInstance flag in __create__ to indicate loopback-swagger
|
||||
|
@ -638,7 +640,7 @@ module.exports = function(registry) {
|
|||
options.replaceOnPUT = options.replaceOnPUT !== false;
|
||||
|
||||
function setRemoting(scope, name, options) {
|
||||
var fn = scope[name];
|
||||
const fn = scope[name];
|
||||
fn._delegate = true;
|
||||
options.isStatic = scope === PersistedModel;
|
||||
PersistedModel.remoteMethod(name, options);
|
||||
|
@ -660,7 +662,7 @@ module.exports = function(registry) {
|
|||
http: {verb: 'post', path: '/'},
|
||||
});
|
||||
|
||||
var upsertOptions = {
|
||||
const upsertOptions = {
|
||||
aliases: ['upsert', 'updateOrCreate'],
|
||||
description: 'Patch an existing model instance or insert a new one ' +
|
||||
'into the data source.',
|
||||
|
@ -681,7 +683,7 @@ module.exports = function(registry) {
|
|||
}
|
||||
setRemoting(PersistedModel, 'patchOrCreate', upsertOptions);
|
||||
|
||||
var replaceOrCreateOptions = {
|
||||
const replaceOrCreateOptions = {
|
||||
description: 'Replace an existing model instance or insert a new one into the data source.',
|
||||
accessType: 'WRITE',
|
||||
accepts: [
|
||||
|
@ -722,7 +724,8 @@ module.exports = function(registry) {
|
|||
description: 'Check whether a model instance exists in the data source.',
|
||||
accessType: 'READ',
|
||||
accepts: [
|
||||
{arg: 'id', type: 'any', description: 'Model id', required: true},
|
||||
{arg: 'id', type: 'any', description: 'Model id', required: true,
|
||||
http: {source: 'path'}},
|
||||
{arg: 'options', type: 'object', http: 'optionsFromRequest'},
|
||||
],
|
||||
returns: {arg: 'exists', type: 'boolean'},
|
||||
|
@ -738,10 +741,10 @@ module.exports = function(registry) {
|
|||
return cb();
|
||||
}
|
||||
if (!ctx.result.exists) {
|
||||
var modelName = ctx.method.sharedClass.name;
|
||||
var id = ctx.getArgByName('id');
|
||||
var msg = 'Unknown "' + modelName + '" id "' + id + '".';
|
||||
var error = new Error(msg);
|
||||
const modelName = ctx.method.sharedClass.name;
|
||||
const id = ctx.getArgByName('id');
|
||||
const msg = 'Unknown "' + modelName + '" id "' + id + '".';
|
||||
const error = new Error(msg);
|
||||
error.statusCode = error.status = 404;
|
||||
error.code = 'MODEL_NOT_FOUND';
|
||||
cb(error);
|
||||
|
@ -769,7 +772,7 @@ module.exports = function(registry) {
|
|||
rest: {after: convertNullToNotFoundError},
|
||||
});
|
||||
|
||||
var replaceByIdOptions = {
|
||||
const replaceByIdOptions = {
|
||||
description: 'Replace attributes for a model instance and persist it into the data source.',
|
||||
accessType: 'WRITE',
|
||||
accepts: [
|
||||
|
@ -795,7 +798,9 @@ module.exports = function(registry) {
|
|||
accepts: [
|
||||
{arg: 'filter', type: 'object', description:
|
||||
'Filter defining fields, where, include, order, offset, and limit - must be a ' +
|
||||
'JSON-encoded string ({"something":"value"})'},
|
||||
'JSON-encoded string (`{"where":{"something":"value"}}`). ' +
|
||||
'See https://loopback.io/doc/en/lb3/Querying-data.html#using-stringified-json-in-rest-queries ' +
|
||||
'for more details.'},
|
||||
{arg: 'options', type: 'object', http: 'optionsFromRequest'},
|
||||
],
|
||||
returns: {arg: 'data', type: [typeName], root: true},
|
||||
|
@ -808,7 +813,9 @@ module.exports = function(registry) {
|
|||
accepts: [
|
||||
{arg: 'filter', type: 'object', description:
|
||||
'Filter defining fields, where, include, order, offset, and limit - must be a ' +
|
||||
'JSON-encoded string ({"something":"value"})'},
|
||||
'JSON-encoded string (`{"where":{"something":"value"}}`). ' +
|
||||
'See https://loopback.io/doc/en/lb3/Querying-data.html#using-stringified-json-in-rest-queries ' +
|
||||
'for more details.'},
|
||||
{arg: 'options', type: 'object', http: 'optionsFromRequest'},
|
||||
],
|
||||
returns: {arg: 'data', type: typeName, root: true},
|
||||
|
@ -882,7 +889,7 @@ module.exports = function(registry) {
|
|||
http: {verb: 'get', path: '/count'},
|
||||
});
|
||||
|
||||
var updateAttributesOptions = {
|
||||
const updateAttributesOptions = {
|
||||
aliases: ['updateAttributes'],
|
||||
description: 'Patch attributes for a model instance and persist it into ' +
|
||||
'the data source.',
|
||||
|
@ -1031,7 +1038,7 @@ module.exports = function(registry) {
|
|||
*/
|
||||
|
||||
PersistedModel.diff = function(since, remoteChanges, callback) {
|
||||
var Change = this.getChangeModel();
|
||||
const Change = this.getChangeModel();
|
||||
Change.diff(this.modelName, since, remoteChanges, callback);
|
||||
};
|
||||
|
||||
|
@ -1057,9 +1064,9 @@ module.exports = function(registry) {
|
|||
filter = {};
|
||||
}
|
||||
|
||||
var idName = this.dataSource.idName(this.modelName);
|
||||
var Change = this.getChangeModel();
|
||||
var model = this;
|
||||
const idName = this.dataSource.idName(this.modelName);
|
||||
const Change = this.getChangeModel();
|
||||
const model = this;
|
||||
const changeFilter = this.createChangeFilter(since, filter);
|
||||
|
||||
filter = filter || {};
|
||||
|
@ -1071,13 +1078,13 @@ module.exports = function(registry) {
|
|||
Change.find(changeFilter, function(err, changes) {
|
||||
if (err) return callback(err);
|
||||
if (!Array.isArray(changes) || changes.length === 0) return callback(null, []);
|
||||
var ids = changes.map(function(change) {
|
||||
const ids = changes.map(function(change) {
|
||||
return change.getModelId();
|
||||
});
|
||||
filter.where[idName] = {inq: ids};
|
||||
model.find(filter, function(err, models) {
|
||||
if (err) return callback(err);
|
||||
var modelIds = models.map(function(m) {
|
||||
const modelIds = models.map(function(m) {
|
||||
return m[idName].toString();
|
||||
});
|
||||
callback(null, changes.filter(function(ch) {
|
||||
|
@ -1095,7 +1102,7 @@ module.exports = function(registry) {
|
|||
*/
|
||||
|
||||
PersistedModel.checkpoint = function(cb) {
|
||||
var Checkpoint = this.getChangeModel().getCheckpointModel();
|
||||
const Checkpoint = this.getChangeModel().getCheckpointModel();
|
||||
Checkpoint.bumpLastSeq(cb);
|
||||
};
|
||||
|
||||
|
@ -1108,7 +1115,7 @@ module.exports = function(registry) {
|
|||
*/
|
||||
|
||||
PersistedModel.currentCheckpoint = function(cb) {
|
||||
var Checkpoint = this.getChangeModel().getCheckpointModel();
|
||||
const Checkpoint = this.getChangeModel().getCheckpointModel();
|
||||
Checkpoint.current(cb);
|
||||
};
|
||||
|
||||
|
@ -1129,7 +1136,7 @@ module.exports = function(registry) {
|
|||
*/
|
||||
|
||||
PersistedModel.replicate = function(since, targetModel, options, callback) {
|
||||
var lastArg = arguments[arguments.length - 1];
|
||||
const lastArg = arguments[arguments.length - 1];
|
||||
|
||||
if (typeof lastArg === 'function' && arguments.length > 1) {
|
||||
callback = lastArg;
|
||||
|
@ -1150,7 +1157,7 @@ module.exports = function(registry) {
|
|||
|
||||
options = options || {};
|
||||
|
||||
var sourceModel = this;
|
||||
const sourceModel = this;
|
||||
callback = callback || utils.createPromiseCallback();
|
||||
|
||||
debug('replicating %s since %s to %s since %s',
|
||||
|
@ -1174,7 +1181,7 @@ module.exports = function(registry) {
|
|||
// until no changes were replicated, but at most MAX_ATTEMPTS times
|
||||
// to prevent starvation. In most cases, the second run will find no changes
|
||||
// to replicate and we are done.
|
||||
var MAX_ATTEMPTS = 3;
|
||||
const MAX_ATTEMPTS = 3;
|
||||
|
||||
run(1, since);
|
||||
return callback.promise;
|
||||
|
@ -1184,7 +1191,7 @@ module.exports = function(registry) {
|
|||
tryReplicate(sourceModel, targetModel, since, options, next);
|
||||
|
||||
function next(err, conflicts, cps, updates) {
|
||||
var finished = err || conflicts.length ||
|
||||
const finished = err || conflicts.length ||
|
||||
!updates || updates.length === 0 ||
|
||||
attempt >= MAX_ATTEMPTS;
|
||||
|
||||
|
@ -1196,10 +1203,10 @@ module.exports = function(registry) {
|
|||
};
|
||||
|
||||
function tryReplicate(sourceModel, targetModel, since, options, callback) {
|
||||
var Change = sourceModel.getChangeModel();
|
||||
var TargetChange = targetModel.getChangeModel();
|
||||
var changeTrackingEnabled = Change && TargetChange;
|
||||
var replicationChunkSize = REPLICATION_CHUNK_SIZE;
|
||||
const Change = sourceModel.getChangeModel();
|
||||
const TargetChange = targetModel.getChangeModel();
|
||||
const changeTrackingEnabled = Change && TargetChange;
|
||||
let replicationChunkSize = REPLICATION_CHUNK_SIZE;
|
||||
|
||||
if (sourceModel.settings && sourceModel.settings.replicationChunkSize) {
|
||||
replicationChunkSize = sourceModel.settings.replicationChunkSize;
|
||||
|
@ -1207,12 +1214,12 @@ module.exports = function(registry) {
|
|||
|
||||
assert(
|
||||
changeTrackingEnabled,
|
||||
'You must enable change tracking before replicating'
|
||||
'You must enable change tracking before replicating',
|
||||
);
|
||||
|
||||
var diff, updates, newSourceCp, newTargetCp;
|
||||
let diff, updates, newSourceCp, newTargetCp;
|
||||
|
||||
var tasks = [
|
||||
const tasks = [
|
||||
checkpoints,
|
||||
getSourceChanges,
|
||||
getDiffFromTarget,
|
||||
|
@ -1229,7 +1236,8 @@ module.exports = function(registry) {
|
|||
function(filter, pagingCallback) {
|
||||
sourceModel.changes(since.source, filter, pagingCallback);
|
||||
},
|
||||
debug.enabled ? log : cb);
|
||||
debug.enabled ? log : cb,
|
||||
);
|
||||
|
||||
function log(err, result) {
|
||||
if (err) return cb(err);
|
||||
|
@ -1246,7 +1254,8 @@ module.exports = function(registry) {
|
|||
function(smallArray, chunkCallback) {
|
||||
return targetModel.diff(since.target, smallArray, chunkCallback);
|
||||
},
|
||||
debug.enabled ? log : cb);
|
||||
debug.enabled ? log : cb,
|
||||
);
|
||||
|
||||
function log(err, result) {
|
||||
if (err) return cb(err);
|
||||
|
@ -1274,7 +1283,8 @@ module.exports = function(registry) {
|
|||
function(smallArray, chunkCallback) {
|
||||
return sourceModel.createUpdates(smallArray, chunkCallback);
|
||||
},
|
||||
cb);
|
||||
cb,
|
||||
);
|
||||
} else {
|
||||
// nothing to replicate
|
||||
done();
|
||||
|
@ -1294,7 +1304,7 @@ module.exports = function(registry) {
|
|||
});
|
||||
},
|
||||
function(notUsed, err) {
|
||||
var conflicts = err && err.details && err.details.conflicts;
|
||||
const conflicts = err && err.details && err.details.conflicts;
|
||||
if (conflicts && err.statusCode == 409) {
|
||||
diff.conflicts = conflicts;
|
||||
// filter out updates that were not applied
|
||||
|
@ -1306,11 +1316,12 @@ module.exports = function(registry) {
|
|||
return cb();
|
||||
}
|
||||
cb(err);
|
||||
});
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
function checkpoints() {
|
||||
var cb = arguments[arguments.length - 1];
|
||||
const cb = arguments[arguments.length - 1];
|
||||
sourceModel.checkpoint(function(err, source) {
|
||||
if (err) return cb(err);
|
||||
newSourceCp = source.seq;
|
||||
|
@ -1334,9 +1345,9 @@ module.exports = function(registry) {
|
|||
debug('\t\tnew checkpoints: { source: %j, target: %j }',
|
||||
newSourceCp, newTargetCp);
|
||||
|
||||
var conflicts = diff.conflicts.map(function(change) {
|
||||
const conflicts = diff.conflicts.map(function(change) {
|
||||
return new Change.Conflict(
|
||||
change.modelId, sourceModel, targetModel
|
||||
change.modelId, sourceModel, targetModel,
|
||||
);
|
||||
});
|
||||
|
||||
|
@ -1345,7 +1356,7 @@ module.exports = function(registry) {
|
|||
}
|
||||
|
||||
if (callback) {
|
||||
var newCheckpoints = {source: newSourceCp, target: newTargetCp};
|
||||
const newCheckpoints = {source: newSourceCp, target: newTargetCp};
|
||||
callback(null, conflicts, newCheckpoints, updates);
|
||||
}
|
||||
}
|
||||
|
@ -1360,15 +1371,15 @@ module.exports = function(registry) {
|
|||
*/
|
||||
|
||||
PersistedModel.createUpdates = function(deltas, cb) {
|
||||
var Change = this.getChangeModel();
|
||||
var updates = [];
|
||||
var Model = this;
|
||||
var tasks = [];
|
||||
const Change = this.getChangeModel();
|
||||
const updates = [];
|
||||
const Model = this;
|
||||
const tasks = [];
|
||||
|
||||
deltas.forEach(function(change) {
|
||||
change = new Change(change);
|
||||
var type = change.type();
|
||||
var update = {type: type, change: change};
|
||||
const type = change.type();
|
||||
const update = {type: type, change: change};
|
||||
switch (type) {
|
||||
case Change.CREATE:
|
||||
case Change.UPDATE:
|
||||
|
@ -1412,12 +1423,12 @@ module.exports = function(registry) {
|
|||
*/
|
||||
|
||||
PersistedModel.bulkUpdate = function(updates, options, callback) {
|
||||
var tasks = [];
|
||||
var Model = this;
|
||||
var Change = this.getChangeModel();
|
||||
var conflicts = [];
|
||||
const tasks = [];
|
||||
const Model = this;
|
||||
const Change = this.getChangeModel();
|
||||
const conflicts = [];
|
||||
|
||||
var lastArg = arguments[arguments.length - 1];
|
||||
const lastArg = arguments[arguments.length - 1];
|
||||
|
||||
if (typeof lastArg === 'function' && arguments.length > 1) {
|
||||
callback = lastArg;
|
||||
|
@ -1433,8 +1444,8 @@ module.exports = function(registry) {
|
|||
if (err) return callback(err);
|
||||
|
||||
updates.forEach(function(update) {
|
||||
var id = update.change.modelId;
|
||||
var current = currentMap[id];
|
||||
const id = update.change.modelId;
|
||||
const current = currentMap[id];
|
||||
switch (update.type) {
|
||||
case Change.UPDATE:
|
||||
tasks.push(function(cb) {
|
||||
|
@ -1469,13 +1480,13 @@ module.exports = function(registry) {
|
|||
};
|
||||
|
||||
function buildLookupOfAffectedModelData(Model, updates, callback) {
|
||||
var idName = Model.dataSource.idName(Model.modelName);
|
||||
var affectedIds = updates.map(function(u) { return u.change.modelId; });
|
||||
var whereAffected = {};
|
||||
const idName = Model.dataSource.idName(Model.modelName);
|
||||
const affectedIds = updates.map(function(u) { return u.change.modelId; });
|
||||
const whereAffected = {};
|
||||
whereAffected[idName] = {inq: affectedIds};
|
||||
Model.find({where: whereAffected}, function(err, affectedList) {
|
||||
if (err) return callback(err);
|
||||
var dataLookup = {};
|
||||
const dataLookup = {};
|
||||
affectedList.forEach(function(it) {
|
||||
dataLookup[it[idName]] = it;
|
||||
});
|
||||
|
@ -1484,8 +1495,8 @@ module.exports = function(registry) {
|
|||
}
|
||||
|
||||
function applyUpdate(Model, id, current, data, change, conflicts, options, cb) {
|
||||
var Change = Model.getChangeModel();
|
||||
var rev = current ? Change.revisionForInst(current) : null;
|
||||
const Change = Model.getChangeModel();
|
||||
const rev = current ? Change.revisionForInst(current) : null;
|
||||
|
||||
if (rev !== change.prev) {
|
||||
debug('Detected non-rectified change of %s %j',
|
||||
|
@ -1504,7 +1515,7 @@ module.exports = function(registry) {
|
|||
Model.updateAll(current.toObject(), data, options, function(err, result) {
|
||||
if (err) return cb(err);
|
||||
|
||||
var count = result && result.count;
|
||||
const count = result && result.count;
|
||||
switch (count) {
|
||||
case 1:
|
||||
// The happy path, exactly one record was updated
|
||||
|
@ -1524,14 +1535,16 @@ module.exports = function(registry) {
|
|||
return cb(new Error(
|
||||
g.f('Cannot apply bulk updates, ' +
|
||||
'the connector does not correctly report ' +
|
||||
'the number of updated records.')));
|
||||
'the number of updated records.'),
|
||||
));
|
||||
|
||||
default:
|
||||
debug('%s.updateAll modified unexpected number of instances: %j',
|
||||
Model.modelName, count);
|
||||
return cb(new Error(
|
||||
g.f('Bulk update failed, the connector has modified unexpected ' +
|
||||
'number of records: %s', JSON.stringify(count))));
|
||||
'number of records: %s', JSON.stringify(count)),
|
||||
));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -1560,7 +1573,7 @@ module.exports = function(registry) {
|
|||
Model.modelName, id);
|
||||
conflicts.push(change);
|
||||
|
||||
var Change = Model.getChangeModel();
|
||||
const Change = Model.getChangeModel();
|
||||
return Change.rectifyModelChanges(Model.modelName, [id], cb);
|
||||
}
|
||||
}
|
||||
|
@ -1572,8 +1585,8 @@ module.exports = function(registry) {
|
|||
return cb();
|
||||
}
|
||||
|
||||
var Change = Model.getChangeModel();
|
||||
var rev = Change.revisionForInst(current);
|
||||
const Change = Model.getChangeModel();
|
||||
const rev = Change.revisionForInst(current);
|
||||
if (rev !== change.prev) {
|
||||
debug('Detected non-rectified change of %s %j',
|
||||
Model.modelName, id);
|
||||
|
@ -1586,7 +1599,7 @@ module.exports = function(registry) {
|
|||
Model.deleteAll(current.toObject(), options, function(err, result) {
|
||||
if (err) return cb(err);
|
||||
|
||||
var count = result && result.count;
|
||||
const count = result && result.count;
|
||||
switch (count) {
|
||||
case 1:
|
||||
// The happy path, exactly one record was updated
|
||||
|
@ -1606,14 +1619,16 @@ module.exports = function(registry) {
|
|||
return cb(new Error(
|
||||
g.f('Cannot apply bulk updates, ' +
|
||||
'the connector does not correctly report ' +
|
||||
'the number of deleted records.')));
|
||||
'the number of deleted records.'),
|
||||
));
|
||||
|
||||
default:
|
||||
debug('%s.deleteAll modified unexpected number of instances: %j',
|
||||
Model.modelName, count);
|
||||
return cb(new Error(
|
||||
g.f('Bulk update failed, the connector has deleted unexpected ' +
|
||||
'number of records: %s', JSON.stringify(count))));
|
||||
'number of records: %s', JSON.stringify(count)),
|
||||
));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -1625,8 +1640,8 @@ module.exports = function(registry) {
|
|||
*/
|
||||
|
||||
PersistedModel.getChangeModel = function() {
|
||||
var changeModel = this.Change;
|
||||
var isSetup = changeModel && changeModel.dataSource;
|
||||
const changeModel = this.Change;
|
||||
const isSetup = changeModel && changeModel.dataSource;
|
||||
|
||||
assert(isSetup, 'Cannot get a setup Change model for ' + this.modelName);
|
||||
|
||||
|
@ -1642,15 +1657,15 @@ module.exports = function(registry) {
|
|||
*/
|
||||
|
||||
PersistedModel.getSourceId = function(cb) {
|
||||
var dataSource = this.dataSource;
|
||||
const dataSource = this.dataSource;
|
||||
if (!dataSource) {
|
||||
this.once('dataSourceAttached', this.getSourceId.bind(this, cb));
|
||||
}
|
||||
assert(
|
||||
dataSource.connector.name,
|
||||
'Model.getSourceId: cannot get id without dataSource.connector.name'
|
||||
'Model.getSourceId: cannot get id without dataSource.connector.name',
|
||||
);
|
||||
var id = [dataSource.connector.name, this.modelName].join('-');
|
||||
const id = [dataSource.connector.name, this.modelName].join('-');
|
||||
cb(null, id);
|
||||
};
|
||||
|
||||
|
@ -1659,17 +1674,17 @@ module.exports = function(registry) {
|
|||
*/
|
||||
|
||||
PersistedModel.enableChangeTracking = function() {
|
||||
var Model = this;
|
||||
var Change = this.Change || this._defineChangeModel();
|
||||
var cleanupInterval = Model.settings.changeCleanupInterval || 30000;
|
||||
const Model = this;
|
||||
const Change = this.Change || this._defineChangeModel();
|
||||
const cleanupInterval = Model.settings.changeCleanupInterval || 30000;
|
||||
|
||||
assert(this.dataSource, 'Cannot enableChangeTracking(): ' + this.modelName +
|
||||
' is not attached to a dataSource');
|
||||
|
||||
var idName = this.getIdName();
|
||||
var idProp = this.definition.properties[idName];
|
||||
var idType = idProp && idProp.type;
|
||||
var idDefn = idProp && idProp.defaultFn;
|
||||
const idName = this.getIdName();
|
||||
const idProp = this.definition.properties[idName];
|
||||
const idType = idProp && idProp.type;
|
||||
const idDefn = idProp && idProp.defaultFn;
|
||||
if (idType !== String || !(idDefn === 'uuid' || idDefn === 'guid')) {
|
||||
deprecated('The model ' + this.modelName + ' is tracking changes, ' +
|
||||
'which requires a string id with GUID/UUID default value.');
|
||||
|
@ -1699,8 +1714,8 @@ module.exports = function(registry) {
|
|||
};
|
||||
|
||||
function rectifyOnSave(ctx, next) {
|
||||
var instance = ctx.instance || ctx.currentInstance;
|
||||
var id = instance ? instance.getId() :
|
||||
const instance = ctx.instance || ctx.currentInstance;
|
||||
const id = instance ? instance.getId() :
|
||||
getIdFromWhereByModelId(ctx.Model, ctx.where);
|
||||
|
||||
if (debug.enabled) {
|
||||
|
@ -1710,7 +1725,7 @@ module.exports = function(registry) {
|
|||
ctx.instance, ctx.currentInstance, ctx.where, ctx.data);
|
||||
}
|
||||
|
||||
if (id) {
|
||||
if (id != null) {
|
||||
ctx.Model.rectifyChange(id, reportErrorAndNext);
|
||||
} else {
|
||||
ctx.Model.rectifyAllChanges(reportErrorAndNext);
|
||||
|
@ -1725,7 +1740,7 @@ module.exports = function(registry) {
|
|||
}
|
||||
|
||||
function rectifyOnDelete(ctx, next) {
|
||||
var id = ctx.instance ? ctx.instance.getId() :
|
||||
const id = ctx.instance ? ctx.instance.getId() :
|
||||
getIdFromWhereByModelId(ctx.Model, ctx.where);
|
||||
|
||||
if (debug.enabled) {
|
||||
|
@ -1734,7 +1749,7 @@ module.exports = function(registry) {
|
|||
debug('context instance:%j where:%j', ctx.instance, ctx.where);
|
||||
}
|
||||
|
||||
if (id) {
|
||||
if (id != null) {
|
||||
ctx.Model.rectifyChange(id, reportErrorAndNext);
|
||||
} else {
|
||||
ctx.Model.rectifyAllChanges(reportErrorAndNext);
|
||||
|
@ -1749,10 +1764,10 @@ module.exports = function(registry) {
|
|||
}
|
||||
|
||||
function getIdFromWhereByModelId(Model, where) {
|
||||
var idName = Model.getIdName();
|
||||
const idName = Model.getIdName();
|
||||
if (!(idName in where)) return undefined;
|
||||
|
||||
var id = where[idName];
|
||||
const id = where[idName];
|
||||
// TODO(bajtos) support object values that are not LB conditions
|
||||
if (typeof id === 'string' || typeof id === 'number') {
|
||||
return id;
|
||||
|
@ -1761,16 +1776,17 @@ module.exports = function(registry) {
|
|||
}
|
||||
|
||||
PersistedModel._defineChangeModel = function() {
|
||||
var BaseChangeModel = this.registry.getModel('Change');
|
||||
const BaseChangeModel = this.registry.getModel('Change');
|
||||
assert(BaseChangeModel,
|
||||
'Change model must be defined before enabling change replication');
|
||||
|
||||
const additionalChangeModelProperties =
|
||||
this.settings.additionalChangeModelProperties || {};
|
||||
|
||||
this.Change = BaseChangeModel.extend(this.modelName + '-change',
|
||||
this.Change = BaseChangeModel.extend(
|
||||
this.modelName + '-change',
|
||||
additionalChangeModelProperties,
|
||||
{trackModel: this}
|
||||
{trackModel: this},
|
||||
);
|
||||
|
||||
if (this.dataSource) {
|
||||
|
@ -1778,7 +1794,7 @@ module.exports = function(registry) {
|
|||
}
|
||||
|
||||
// Re-attach related models whenever our datasource is changed.
|
||||
var self = this;
|
||||
const self = this;
|
||||
this.on('dataSourceAttached', function() {
|
||||
attachRelatedModels(self);
|
||||
});
|
||||
|
@ -1816,17 +1832,17 @@ module.exports = function(registry) {
|
|||
*/
|
||||
|
||||
PersistedModel.rectifyChange = function(id, callback) {
|
||||
var Change = this.getChangeModel();
|
||||
const Change = this.getChangeModel();
|
||||
Change.rectifyModelChanges(this.modelName, [id], callback);
|
||||
};
|
||||
|
||||
PersistedModel.findLastChange = function(id, cb) {
|
||||
var Change = this.getChangeModel();
|
||||
const Change = this.getChangeModel();
|
||||
Change.findOne({where: {modelId: id}}, cb);
|
||||
};
|
||||
|
||||
PersistedModel.updateLastChange = function(id, data, cb) {
|
||||
var self = this;
|
||||
const self = this;
|
||||
this.findLastChange(id, function(err, inst) {
|
||||
if (err) return cb(err);
|
||||
if (!inst) {
|
||||
|
@ -1855,10 +1871,11 @@ module.exports = function(registry) {
|
|||
cb = options;
|
||||
options = undefined;
|
||||
}
|
||||
cb = cb || utils.createPromiseCallback();
|
||||
|
||||
var idName = this.getIdName();
|
||||
var Model = this;
|
||||
var changes = new PassThrough({objectMode: true});
|
||||
const idName = this.getIdName();
|
||||
const Model = this;
|
||||
const changes = new PassThrough({objectMode: true});
|
||||
|
||||
changes._destroy = function() {
|
||||
changes.end();
|
||||
|
@ -1880,28 +1897,34 @@ module.exports = function(registry) {
|
|||
Model.observe('after save', changeHandler);
|
||||
Model.observe('after delete', deleteHandler);
|
||||
|
||||
return cb.promise;
|
||||
|
||||
function changeHandler(ctx, next) {
|
||||
var change = createChangeObject(ctx, 'save');
|
||||
changes.write(change);
|
||||
const change = createChangeObject(ctx, 'save');
|
||||
if (change) {
|
||||
changes.write(change);
|
||||
}
|
||||
|
||||
next();
|
||||
};
|
||||
}
|
||||
|
||||
function deleteHandler(ctx, next) {
|
||||
var change = createChangeObject(ctx, 'delete');
|
||||
changes.write(change);
|
||||
const change = createChangeObject(ctx, 'delete');
|
||||
if (change) {
|
||||
changes.write(change);
|
||||
}
|
||||
|
||||
next();
|
||||
};
|
||||
}
|
||||
|
||||
function createChangeObject(ctx, type) {
|
||||
var where = ctx.where;
|
||||
var data = ctx.instance || ctx.data;
|
||||
var whereId = where && where[idName];
|
||||
const where = ctx.where;
|
||||
let data = ctx.instance || ctx.data;
|
||||
const whereId = where && where[idName];
|
||||
|
||||
// the data includes the id
|
||||
// or the where includes the id
|
||||
var target;
|
||||
let target;
|
||||
|
||||
if (data && (data[idName] || data[idName] === 0)) {
|
||||
target = data[idName];
|
||||
|
@ -1909,9 +1932,18 @@ module.exports = function(registry) {
|
|||
target = where[idName];
|
||||
}
|
||||
|
||||
var hasTarget = target === 0 || !!target;
|
||||
const hasTarget = target === 0 || !!target;
|
||||
|
||||
var change = {
|
||||
// apply filtering if options is set
|
||||
if (options) {
|
||||
const filtered = filterNodes([data], options);
|
||||
if (filtered.length !== 1) {
|
||||
return null;
|
||||
}
|
||||
data = filtered[0];
|
||||
}
|
||||
|
||||
const change = {
|
||||
target: target,
|
||||
where: where,
|
||||
data: data,
|
||||
|
|
|
@ -1,17 +1,17 @@
|
|||
// Copyright IBM Corp. 2014,2016. All Rights Reserved.
|
||||
// Copyright IBM Corp. 2014,2019. All Rights Reserved.
|
||||
// Node module: loopback
|
||||
// This file is licensed under the MIT License.
|
||||
// License text available at https://opensource.org/licenses/MIT
|
||||
|
||||
'use strict';
|
||||
var g = require('./globalize');
|
||||
var assert = require('assert');
|
||||
var extend = require('util')._extend;
|
||||
var juggler = require('loopback-datasource-juggler');
|
||||
var debug = require('debug')('loopback:registry');
|
||||
var DataSource = juggler.DataSource;
|
||||
var ModelBuilder = juggler.ModelBuilder;
|
||||
var deprecated = require('depd')('strong-remoting');
|
||||
const g = require('./globalize');
|
||||
const assert = require('assert');
|
||||
const extend = require('util')._extend;
|
||||
const juggler = require('loopback-datasource-juggler');
|
||||
const debug = require('debug')('loopback:registry');
|
||||
const DataSource = juggler.DataSource;
|
||||
const ModelBuilder = juggler.ModelBuilder;
|
||||
const deprecated = require('depd')('strong-remoting');
|
||||
|
||||
module.exports = Registry;
|
||||
|
||||
|
@ -97,7 +97,7 @@ function Registry() {
|
|||
|
||||
Registry.prototype.createModel = function(name, properties, options) {
|
||||
if (arguments.length === 1 && typeof name === 'object') {
|
||||
var config = name;
|
||||
const config = name;
|
||||
name = config.name;
|
||||
properties = config.properties;
|
||||
options = buildModelOptionsFromConfig(config);
|
||||
|
@ -107,10 +107,10 @@ Registry.prototype.createModel = function(name, properties, options) {
|
|||
}
|
||||
|
||||
options = options || {};
|
||||
var BaseModel = options.base || options.super;
|
||||
let BaseModel = options.base || options.super;
|
||||
|
||||
if (typeof BaseModel === 'string') {
|
||||
var baseName = BaseModel;
|
||||
const baseName = BaseModel;
|
||||
BaseModel = this.findModel(BaseModel);
|
||||
if (!BaseModel) {
|
||||
throw new Error(g.f('Model not found: model `%s` is extending an unknown model `%s`.',
|
||||
|
@ -119,7 +119,7 @@ Registry.prototype.createModel = function(name, properties, options) {
|
|||
}
|
||||
|
||||
BaseModel = BaseModel || this.getModel('PersistedModel');
|
||||
var model = BaseModel.extend(name, properties, options);
|
||||
const model = BaseModel.extend(name, properties, options);
|
||||
model.registry = this;
|
||||
|
||||
this._defineRemoteMethods(model, model.settings.methods);
|
||||
|
@ -128,8 +128,8 @@ Registry.prototype.createModel = function(name, properties, options) {
|
|||
};
|
||||
|
||||
function buildModelOptionsFromConfig(config) {
|
||||
var options = extend({}, config.options);
|
||||
for (var key in config) {
|
||||
const options = extend({}, config.options);
|
||||
for (const key in config) {
|
||||
if (['name', 'properties', 'options'].indexOf(key) !== -1) {
|
||||
// Skip items which have special meaning
|
||||
continue;
|
||||
|
@ -152,7 +152,7 @@ function buildModelOptionsFromConfig(config) {
|
|||
* @param {Object} acl
|
||||
*/
|
||||
function addACL(acls, acl) {
|
||||
for (var i = 0, n = acls.length; i < n; i++) {
|
||||
for (let i = 0, n = acls.length; i < n; i++) {
|
||||
// Check if there is a matching acl to be overriden
|
||||
if (acls[i].property === acl.property &&
|
||||
acls[i].accessType === acl.accessType &&
|
||||
|
@ -176,12 +176,14 @@ function addACL(acls, acl) {
|
|||
*/
|
||||
|
||||
Registry.prototype.configureModel = function(ModelCtor, config) {
|
||||
var settings = ModelCtor.settings;
|
||||
var modelName = ModelCtor.modelName;
|
||||
const settings = ModelCtor.settings;
|
||||
const modelName = ModelCtor.modelName;
|
||||
|
||||
ModelCtor.config = config;
|
||||
|
||||
// Relations
|
||||
if (typeof config.relations === 'object' && config.relations !== null) {
|
||||
var relations = settings.relations = settings.relations || {};
|
||||
const relations = settings.relations = settings.relations || {};
|
||||
Object.keys(config.relations).forEach(function(key) {
|
||||
// FIXME: [rfeng] We probably should check if the relation exists
|
||||
relations[key] = extend(relations[key] || {}, config.relations[key]);
|
||||
|
@ -193,7 +195,7 @@ Registry.prototype.configureModel = function(ModelCtor, config) {
|
|||
|
||||
// ACLs
|
||||
if (Array.isArray(config.acls)) {
|
||||
var acls = settings.acls = settings.acls || [];
|
||||
const acls = settings.acls = settings.acls || [];
|
||||
config.acls.forEach(function(acl) {
|
||||
addACL(acls, acl);
|
||||
});
|
||||
|
@ -203,7 +205,7 @@ Registry.prototype.configureModel = function(ModelCtor, config) {
|
|||
}
|
||||
|
||||
// Settings
|
||||
var excludedProperties = {
|
||||
const excludedProperties = {
|
||||
base: true,
|
||||
'super': true,
|
||||
relations: true,
|
||||
|
@ -211,7 +213,7 @@ Registry.prototype.configureModel = function(ModelCtor, config) {
|
|||
dataSource: true,
|
||||
};
|
||||
if (typeof config.options === 'object' && config.options !== null) {
|
||||
for (var p in config.options) {
|
||||
for (const p in config.options) {
|
||||
if (!(p in excludedProperties)) {
|
||||
settings[p] = config.options[p];
|
||||
} else {
|
||||
|
@ -228,7 +230,7 @@ Registry.prototype.configureModel = function(ModelCtor, config) {
|
|||
// configuration, so that the datasource picks up updated relations
|
||||
if (config.dataSource) {
|
||||
assert(config.dataSource instanceof DataSource,
|
||||
'Cannot configure ' + ModelCtor.modelName +
|
||||
'Cannot configure ' + ModelCtor.modelName +
|
||||
': config.dataSource must be an instance of DataSource');
|
||||
ModelCtor.attachTo(config.dataSource);
|
||||
debug('Attached model `%s` to dataSource `%s`',
|
||||
|
@ -242,16 +244,18 @@ Registry.prototype.configureModel = function(ModelCtor, config) {
|
|||
g.warn(
|
||||
'The configuration of `%s` is missing {{`dataSource`}} property.\n' +
|
||||
'Use `null` or `false` to mark models not attached to any data source.',
|
||||
modelName);
|
||||
modelName,
|
||||
);
|
||||
}
|
||||
|
||||
var newMethodNames = config.methods && Object.keys(config.methods);
|
||||
var hasNewMethods = newMethodNames && newMethodNames.length;
|
||||
var hasDescendants = this.getModelByType(ModelCtor) !== ModelCtor;
|
||||
const newMethodNames = config.methods && Object.keys(config.methods);
|
||||
const hasNewMethods = newMethodNames && newMethodNames.length;
|
||||
const hasDescendants = this.getModelByType(ModelCtor) !== ModelCtor;
|
||||
if (hasNewMethods && hasDescendants) {
|
||||
g.warn(
|
||||
'Child models of `%s` will not inherit newly defined remote methods %s.',
|
||||
modelName, newMethodNames);
|
||||
modelName, newMethodNames,
|
||||
);
|
||||
}
|
||||
|
||||
// Remote methods
|
||||
|
@ -267,9 +271,9 @@ Registry.prototype._defineRemoteMethods = function(ModelCtor, methods) {
|
|||
}
|
||||
|
||||
Object.keys(methods).forEach(function(key) {
|
||||
var meta = methods[key];
|
||||
var m = key.match(/^prototype\.(.*)$/);
|
||||
var isStatic = !m;
|
||||
let meta = methods[key];
|
||||
const m = key.match(/^prototype\.(.*)$/);
|
||||
const isStatic = !m;
|
||||
|
||||
if (typeof meta.isStatic !== 'boolean') {
|
||||
key = isStatic ? key : m[1];
|
||||
|
@ -309,7 +313,7 @@ Registry.prototype.findModel = function(modelName) {
|
|||
* @header loopback.getModel(modelName)
|
||||
*/
|
||||
Registry.prototype.getModel = function(modelName) {
|
||||
var model = this.findModel(modelName);
|
||||
const model = this.findModel(modelName);
|
||||
if (model) return model;
|
||||
|
||||
throw new Error(g.f('Model not found: %s', modelName));
|
||||
|
@ -325,8 +329,8 @@ Registry.prototype.getModel = function(modelName) {
|
|||
* @header loopback.getModelByType(modelType)
|
||||
*/
|
||||
Registry.prototype.getModelByType = function(modelType) {
|
||||
var type = typeof modelType;
|
||||
var accepted = ['function', 'string'];
|
||||
const type = typeof modelType;
|
||||
const accepted = ['function', 'string'];
|
||||
|
||||
assert(accepted.indexOf(type) > -1,
|
||||
'The model type must be a constructor or model name');
|
||||
|
@ -335,8 +339,8 @@ Registry.prototype.getModelByType = function(modelType) {
|
|||
modelType = this.getModel(modelType);
|
||||
}
|
||||
|
||||
var models = this.modelBuilder.models;
|
||||
for (var m in models) {
|
||||
const models = this.modelBuilder.models;
|
||||
for (const m in models) {
|
||||
if (models[m].prototype instanceof modelType) {
|
||||
return models[m];
|
||||
}
|
||||
|
@ -355,15 +359,15 @@ Registry.prototype.getModelByType = function(modelType) {
|
|||
*/
|
||||
|
||||
Registry.prototype.createDataSource = function(name, options) {
|
||||
var self = this;
|
||||
const self = this;
|
||||
|
||||
var ds = new DataSource(name, options, self.modelBuilder);
|
||||
const ds = new DataSource(name, options, self.modelBuilder);
|
||||
ds.createModel = function(name, properties, settings) {
|
||||
settings = settings || {};
|
||||
var BaseModel = settings.base || settings.super;
|
||||
let BaseModel = settings.base || settings.super;
|
||||
if (!BaseModel) {
|
||||
// Check the connector types
|
||||
var connectorTypes = ds.getTypes();
|
||||
const connectorTypes = ds.getTypes();
|
||||
if (Array.isArray(connectorTypes) && connectorTypes.indexOf('db') !== -1) {
|
||||
// Only set up the base model to PersistedModel if the connector is DB
|
||||
BaseModel = self.PersistedModel;
|
||||
|
@ -372,13 +376,13 @@ Registry.prototype.createDataSource = function(name, options) {
|
|||
}
|
||||
settings.base = BaseModel;
|
||||
}
|
||||
var ModelCtor = self.createModel(name, properties, settings);
|
||||
const ModelCtor = self.createModel(name, properties, settings);
|
||||
ModelCtor.attachTo(ds);
|
||||
return ModelCtor;
|
||||
};
|
||||
|
||||
if (ds.settings && ds.settings.defaultForType) {
|
||||
var msg = g.f('{{DataSource}} option {{"defaultForType"}} is no longer supported');
|
||||
const msg = g.f('{{DataSource}} option {{"defaultForType"}} is no longer supported');
|
||||
throw new Error(msg);
|
||||
}
|
||||
|
||||
|
@ -394,9 +398,9 @@ Registry.prototype.createDataSource = function(name, options) {
|
|||
|
||||
Registry.prototype.memory = function(name) {
|
||||
name = name || 'default';
|
||||
var memory = (
|
||||
let memory = (
|
||||
this._memoryDataSources || (this._memoryDataSources = {})
|
||||
)[name];
|
||||
)[name];
|
||||
|
||||
if (!memory) {
|
||||
memory = this._memoryDataSources[name] = this.createDataSource({
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// Copyright IBM Corp. 2014. All Rights Reserved.
|
||||
// Copyright IBM Corp. 2014,2019. All Rights Reserved.
|
||||
// Node module: loopback
|
||||
// This file is licensed under the MIT License.
|
||||
// License text available at https://opensource.org/licenses/MIT
|
||||
|
@ -10,7 +10,7 @@
|
|||
*/
|
||||
|
||||
'use strict';
|
||||
var runtime = exports;
|
||||
const runtime = exports;
|
||||
|
||||
/**
|
||||
* True if running in a browser environment; false otherwise.
|
||||
|
|
|
@ -1,23 +1,23 @@
|
|||
// Copyright IBM Corp. 2014,2016. All Rights Reserved.
|
||||
// Copyright IBM Corp. 2014,2019. All Rights Reserved.
|
||||
// Node module: loopback
|
||||
// This file is licensed under the MIT License.
|
||||
// License text available at https://opensource.org/licenses/MIT
|
||||
|
||||
'use strict';
|
||||
var g = require('./globalize');
|
||||
var assert = require('assert');
|
||||
var express = require('express');
|
||||
var merge = require('util')._extend;
|
||||
var mergePhaseNameLists = require('loopback-phase').mergePhaseNameLists;
|
||||
var debug = require('debug')('loopback:app');
|
||||
var stableSortInPlace = require('stable').inplace;
|
||||
const g = require('./globalize');
|
||||
const assert = require('assert');
|
||||
const express = require('express');
|
||||
const merge = require('util')._extend;
|
||||
const mergePhaseNameLists = require('loopback-phase').mergePhaseNameLists;
|
||||
const debug = require('debug')('loopback:app');
|
||||
const stableSortInPlace = require('stable').inplace;
|
||||
|
||||
var BUILTIN_MIDDLEWARE = {builtin: true};
|
||||
const BUILTIN_MIDDLEWARE = {builtin: true};
|
||||
|
||||
var proto = {};
|
||||
const proto = {};
|
||||
|
||||
module.exports = function loopbackExpress() {
|
||||
var app = express();
|
||||
const app = express();
|
||||
app.__expressLazyRouter = app.lazyrouter;
|
||||
merge(app, proto);
|
||||
return app;
|
||||
|
@ -64,23 +64,23 @@ proto.middlewareFromConfig = function(factory, config) {
|
|||
if (config.enabled === false)
|
||||
return;
|
||||
|
||||
var params = config.params;
|
||||
let params = config.params;
|
||||
if (params === undefined) {
|
||||
params = [];
|
||||
} else if (!Array.isArray(params)) {
|
||||
params = [params];
|
||||
}
|
||||
|
||||
var handler = factory.apply(null, params);
|
||||
let handler = factory.apply(null, params);
|
||||
|
||||
// Check if methods/verbs filter exists
|
||||
var verbs = config.methods || config.verbs;
|
||||
let verbs = config.methods || config.verbs;
|
||||
if (Array.isArray(verbs)) {
|
||||
verbs = verbs.map(function(verb) {
|
||||
return verb && verb.toUpperCase();
|
||||
});
|
||||
if (verbs.indexOf('ALL') === -1) {
|
||||
var originalHandler = handler;
|
||||
const originalHandler = handler;
|
||||
if (handler.length <= 3) {
|
||||
// Regular handler
|
||||
handler = function(req, res, next) {
|
||||
|
@ -145,7 +145,7 @@ proto.defineMiddlewarePhases = function(nameOrArray) {
|
|||
mergePhaseNameLists(this._requestHandlingPhases, nameOrArray);
|
||||
} else {
|
||||
// add the new phase before 'routes'
|
||||
var routesIx = this._requestHandlingPhases.indexOf('routes');
|
||||
const routesIx = this._requestHandlingPhases.indexOf('routes');
|
||||
this._requestHandlingPhases.splice(routesIx - 1, 0, nameOrArray);
|
||||
}
|
||||
|
||||
|
@ -181,10 +181,10 @@ proto.middleware = function(name, paths, handler) {
|
|||
paths = '/';
|
||||
}
|
||||
|
||||
var fullPhaseName = name;
|
||||
var handlerName = handler.name || '<anonymous>';
|
||||
const fullPhaseName = name;
|
||||
const handlerName = handler.name || '<anonymous>';
|
||||
|
||||
var m = name.match(/^(.+):(before|after)$/);
|
||||
const m = name.match(/^(.+):(before|after)$/);
|
||||
if (m) {
|
||||
name = m[1];
|
||||
}
|
||||
|
@ -197,7 +197,7 @@ proto.middleware = function(name, paths, handler) {
|
|||
this._skipLayerSorting = true;
|
||||
this.use(paths, handler);
|
||||
|
||||
var layer = this._findLayerByHandler(handler);
|
||||
const layer = this._findLayerByHandler(handler);
|
||||
if (layer) {
|
||||
// Set the phase name for sorting
|
||||
layer.phase = fullPhaseName;
|
||||
|
@ -221,17 +221,20 @@ proto.middleware = function(name, paths, handler) {
|
|||
*/
|
||||
proto._findLayerByHandler = function(handler) {
|
||||
// Other handlers can be added to the stack, for example,
|
||||
// NewRelic adds sentinel handler. We need to search the stack
|
||||
for (var k = this._router.stack.length - 1; k >= 0; k--) {
|
||||
if (this._router.stack[k].handle === handler ||
|
||||
// NewRelic replaces the handle and keeps it as __NR_original
|
||||
this._router.stack[k].handle['__NR_original'] === handler
|
||||
) {
|
||||
// NewRelic adds sentinel handler, and AppDynamics adds
|
||||
// some additional proxy info. We need to search the stack
|
||||
for (let k = this._router.stack.length - 1; k >= 0; k--) {
|
||||
const isOriginal = this._router.stack[k].handle === handler;
|
||||
const isNewRelic = this._router.stack[k].handle['__NR_original'] === handler;
|
||||
const isAppDynamics = this._router.stack[k].handle['__appdynamicsProxyInfo__'] &&
|
||||
this._router.stack[k].handle['__appdynamicsProxyInfo__']['orig'] === handler;
|
||||
|
||||
if (isOriginal || isNewRelic || isAppDynamics) {
|
||||
return this._router.stack[k];
|
||||
} else {
|
||||
// Aggressively check if the original handler has been wrapped
|
||||
// into a new function with a property pointing to the original handler
|
||||
for (var p in this._router.stack[k].handle) {
|
||||
for (const p in this._router.stack[k].handle) {
|
||||
if (this._router.stack[k].handle[p] === handler) {
|
||||
return this._router.stack[k];
|
||||
}
|
||||
|
@ -243,12 +246,12 @@ proto._findLayerByHandler = function(handler) {
|
|||
|
||||
// Install our custom PhaseList-based handler into the app
|
||||
proto.lazyrouter = function() {
|
||||
var self = this;
|
||||
const self = this;
|
||||
if (self._router) return;
|
||||
|
||||
self.__expressLazyRouter();
|
||||
|
||||
var router = self._router;
|
||||
const router = self._router;
|
||||
|
||||
// Mark all middleware added by Router ctor as builtin
|
||||
// The sorting algo will keep them at beginning of the list
|
||||
|
@ -258,14 +261,14 @@ proto.lazyrouter = function() {
|
|||
|
||||
router.__expressUse = router.use;
|
||||
router.use = function useAndSort() {
|
||||
var retval = this.__expressUse.apply(this, arguments);
|
||||
const retval = this.__expressUse.apply(this, arguments);
|
||||
self._sortLayersByPhase();
|
||||
return retval;
|
||||
};
|
||||
|
||||
router.__expressRoute = router.route;
|
||||
router.route = function routeAndSort() {
|
||||
var retval = this.__expressRoute.apply(this, arguments);
|
||||
const retval = this.__expressRoute.apply(this, arguments);
|
||||
self._sortLayersByPhase();
|
||||
return retval;
|
||||
};
|
||||
|
@ -279,19 +282,19 @@ proto.lazyrouter = function() {
|
|||
proto._sortLayersByPhase = function() {
|
||||
if (this._skipLayerSorting) return;
|
||||
|
||||
var phaseOrder = {};
|
||||
const phaseOrder = {};
|
||||
this._requestHandlingPhases.forEach(function(name, ix) {
|
||||
phaseOrder[name + ':before'] = ix * 3;
|
||||
phaseOrder[name] = ix * 3 + 1;
|
||||
phaseOrder[name + ':after'] = ix * 3 + 2;
|
||||
});
|
||||
|
||||
var router = this._router;
|
||||
const router = this._router;
|
||||
stableSortInPlace(router.stack, compareLayers);
|
||||
|
||||
function compareLayers(left, right) {
|
||||
var leftPhase = left.phase;
|
||||
var rightPhase = right.phase;
|
||||
const leftPhase = left.phase;
|
||||
const rightPhase = right.phase;
|
||||
|
||||
if (leftPhase === rightPhase) return 0;
|
||||
|
||||
|
|
23
lib/utils.js
23
lib/utils.js
|
@ -1,4 +1,4 @@
|
|||
// Copyright IBM Corp. 2015. All Rights Reserved.
|
||||
// Copyright IBM Corp. 2015,2019. All Rights Reserved.
|
||||
// Node module: loopback
|
||||
// This file is licensed under the MIT License.
|
||||
// License text available at https://opensource.org/licenses/MIT
|
||||
|
@ -10,12 +10,12 @@ exports.uploadInChunks = uploadInChunks;
|
|||
exports.downloadInChunks = downloadInChunks;
|
||||
exports.concatResults = concatResults;
|
||||
|
||||
var Promise = require('bluebird');
|
||||
var async = require('async');
|
||||
const Promise = require('bluebird');
|
||||
const async = require('async');
|
||||
|
||||
function createPromiseCallback() {
|
||||
var cb;
|
||||
var promise = new Promise(function(resolve, reject) {
|
||||
let cb;
|
||||
const promise = new Promise(function(resolve, reject) {
|
||||
cb = function(err, data) {
|
||||
if (err) return reject(err);
|
||||
return resolve(data);
|
||||
|
@ -28,7 +28,8 @@ function createPromiseCallback() {
|
|||
function throwPromiseNotDefined() {
|
||||
throw new Error(
|
||||
'Your Node runtime does support ES6 Promises. ' +
|
||||
'Set "global.Promise" to your preferred implementation of promises.');
|
||||
'Set "global.Promise" to your preferred implementation of promises.',
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -39,23 +40,23 @@ function throwPromiseNotDefined() {
|
|||
* @param {Function} cb - the callback
|
||||
*/
|
||||
function uploadInChunks(largeArray, chunkSize, processFunction, cb) {
|
||||
var chunkArrays = [];
|
||||
const chunkArrays = [];
|
||||
|
||||
if (!chunkSize || chunkSize < 1 || largeArray.length <= chunkSize) {
|
||||
// if chunking not required
|
||||
processFunction(largeArray, cb);
|
||||
} else {
|
||||
// copying so that the largeArray object does not get affected during splice
|
||||
var copyOfLargeArray = [].concat(largeArray);
|
||||
const copyOfLargeArray = [].concat(largeArray);
|
||||
|
||||
// chunking to smaller arrays
|
||||
while (copyOfLargeArray.length > 0) {
|
||||
chunkArrays.push(copyOfLargeArray.splice(0, chunkSize));
|
||||
}
|
||||
|
||||
var tasks = chunkArrays.map(function(chunkArray) {
|
||||
const tasks = chunkArrays.map(function(chunkArray) {
|
||||
return function(previousResults, chunkCallback) {
|
||||
var lastArg = arguments[arguments.length - 1];
|
||||
const lastArg = arguments[arguments.length - 1];
|
||||
|
||||
if (typeof lastArg === 'function') {
|
||||
chunkCallback = lastArg;
|
||||
|
@ -91,7 +92,7 @@ function uploadInChunks(largeArray, chunkSize, processFunction, cb) {
|
|||
* @param {Function} cb - the callback
|
||||
*/
|
||||
function downloadInChunks(filter, chunkSize, processFunction, cb) {
|
||||
var results = [];
|
||||
let results = [];
|
||||
filter = filter ? JSON.parse(JSON.stringify(filter)) : {};
|
||||
|
||||
if (!chunkSize || chunkSize < 1) {
|
||||
|
|
78
package.json
78
package.json
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "loopback",
|
||||
"version": "3.11.1",
|
||||
"version": "3.28.0",
|
||||
"description": "LoopBack: Open Source Framework for Node.js",
|
||||
"homepage": "http://loopback.io",
|
||||
"keywords": [
|
||||
|
@ -34,7 +34,7 @@
|
|||
"test": "nyc grunt mocha-and-karma"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=4.0.0"
|
||||
"node": ">=8"
|
||||
},
|
||||
"dependencies": {
|
||||
"async": "^2.0.1",
|
||||
|
@ -47,57 +47,58 @@
|
|||
"ejs": "^2.3.1",
|
||||
"express": "^4.14.0",
|
||||
"inflection": "^1.6.0",
|
||||
"isemail": "^2.2.1",
|
||||
"isemail": "^3.2.0",
|
||||
"loopback-connector-remote": "^3.0.0",
|
||||
"loopback-datasource-juggler": "^3.9.3",
|
||||
"loopback-datasource-juggler": "^3.28.0",
|
||||
"loopback-filters": "^1.0.0",
|
||||
"loopback-phase": "^3.0.0",
|
||||
"nodemailer": "^2.5.0",
|
||||
"nodemailer-stub-transport": "^1.0.0",
|
||||
"nodemailer": "^6.4.16",
|
||||
"nodemailer-direct-transport": "^3.3.2",
|
||||
"nodemailer-stub-transport": "^1.1.0",
|
||||
"serve-favicon": "^2.2.0",
|
||||
"stable": "^0.1.5",
|
||||
"strong-globalize": "^2.7.0",
|
||||
"strong-remoting": "^3.0.0",
|
||||
"strong-globalize": "^4.1.1",
|
||||
"strong-remoting": "^3.11.0",
|
||||
"uid2": "0.0.3",
|
||||
"underscore.string": "^3.0.3"
|
||||
"underscore.string": "^3.3.5"
|
||||
},
|
||||
"devDependencies": {
|
||||
"babel-preset-es2015": "^6.22.0",
|
||||
"babelify": "^7.3.0",
|
||||
"browserify": "^13.1.0",
|
||||
"chai": "^3.5.0",
|
||||
"browserify": "^16.5.0",
|
||||
"chai": "^4.2.0",
|
||||
"cookie-parser": "^1.3.4",
|
||||
"coveralls": "^2.11.15",
|
||||
"dirty-chai": "^1.2.2",
|
||||
"eslint-config-loopback": "^8.0.0",
|
||||
"coveralls": "^3.0.2",
|
||||
"dirty-chai": "^2.0.1",
|
||||
"eslint": "^6.5.1",
|
||||
"eslint-config-loopback": "^13.1.0",
|
||||
"express-session": "^1.14.0",
|
||||
"grunt": "^1.0.1",
|
||||
"grunt-browserify": "^5.0.0",
|
||||
"grunt-cli": "^1.2.0",
|
||||
"grunt-contrib-uglify": "^2.0.0",
|
||||
"grunt-contrib-uglify": "^4.0.1",
|
||||
"grunt-contrib-watch": "^1.0.0",
|
||||
"grunt-eslint": "^19.0.0",
|
||||
"grunt-karma": "^2.0.0",
|
||||
"grunt-mocha-test": "^0.12.7",
|
||||
"karma": "^1.1.2",
|
||||
"karma-browserify": "^5.1.1",
|
||||
"karma-chrome-launcher": "^1.0.1",
|
||||
"grunt-eslint": "^22.0.0",
|
||||
"grunt-karma": "^3.0.2",
|
||||
"grunt-mocha-test": "^0.13.3",
|
||||
"is-docker": "^2.0.0",
|
||||
"karma": "^4.1.0",
|
||||
"karma-browserify": "^6.0.0",
|
||||
"karma-chrome-launcher": "^3.1.0",
|
||||
"karma-es6-shim": "^1.0.0",
|
||||
"karma-firefox-launcher": "^1.0.0",
|
||||
"karma-html2js-preprocessor": "^1.0.0",
|
||||
"karma-junit-reporter": "~1.0.0",
|
||||
"karma-junit-reporter": "^1.2.0",
|
||||
"karma-mocha": "^1.1.1",
|
||||
"karma-phantomjs-launcher": "^1.0.0",
|
||||
"karma-script-launcher": "^1.0.0",
|
||||
"loopback-boot": "^2.7.0",
|
||||
"loopback-context": "^1.0.0",
|
||||
"mocha": "^3.0.0",
|
||||
"nyc": "^10.1.2",
|
||||
"phantomjs-prebuilt": "^2.1.7",
|
||||
"sinon": "^1.13.0",
|
||||
"sinon-chai": "^2.8.0",
|
||||
"strong-error-handler": "^2.1.0",
|
||||
"strong-task-emitter": "^0.0.6",
|
||||
"supertest": "^3.0.0"
|
||||
"mocha": "^6.2.1",
|
||||
"nyc": "^14.1.1",
|
||||
"sinon": "^7.5.0",
|
||||
"sinon-chai": "^3.2.0",
|
||||
"strong-error-handler": "^3.0.0",
|
||||
"strong-task-emitter": "^0.0.8",
|
||||
"supertest": "^4.0.2",
|
||||
"which": "^2.0.1"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
|
@ -117,5 +118,14 @@
|
|||
"debug": "*,-mocha:*,-eslint:*"
|
||||
}
|
||||
},
|
||||
"license": "MIT"
|
||||
"copyright.owner": "IBM Corp.",
|
||||
"license": "MIT",
|
||||
"author": "IBM Corp.",
|
||||
"ci": {
|
||||
"downstreamIgnoreList": [
|
||||
"bluemix-service-broker",
|
||||
"gateway-director-bluemix",
|
||||
"plan-manager"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,14 +1,15 @@
|
|||
// Copyright IBM Corp. 2014,2016. All Rights Reserved.
|
||||
// Copyright IBM Corp. 2014,2019. All Rights Reserved.
|
||||
// Node module: loopback
|
||||
// This file is licensed under the MIT License.
|
||||
// License text available at https://opensource.org/licenses/MIT
|
||||
|
||||
'use strict';
|
||||
var g = require('../../lib/globalize');
|
||||
const g = require('../../lib/globalize');
|
||||
|
||||
module.exports = function() {
|
||||
throw new Error(g.f(
|
||||
'%s middleware was removed in version 3.0. See %s for more details.',
|
||||
'loopback#context',
|
||||
'http://loopback.io/doc/en/lb2/Using-current-context.html'));
|
||||
'http://loopback.io/doc/en/lb2/Using-current-context.html',
|
||||
));
|
||||
};
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// Copyright IBM Corp. 2015. All Rights Reserved.
|
||||
// Copyright IBM Corp. 2015,2018. All Rights Reserved.
|
||||
// Node module: loopback
|
||||
// This file is licensed under the MIT License.
|
||||
// License text available at https://opensource.org/licenses/MIT
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
// Copyright IBM Corp. 2014. All Rights Reserved.
|
||||
// Copyright IBM Corp. 2014,2019. All Rights Reserved.
|
||||
// Node module: loopback
|
||||
// This file is licensed under the MIT License.
|
||||
// License text available at https://opensource.org/licenses/MIT
|
||||
|
||||
'use strict';
|
||||
var favicon = require('serve-favicon');
|
||||
var path = require('path');
|
||||
const favicon = require('serve-favicon');
|
||||
const path = require('path');
|
||||
|
||||
/**
|
||||
* Serve the LoopBack favicon.
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// Copyright IBM Corp. 2014,2015. All Rights Reserved.
|
||||
// Copyright IBM Corp. 2014,2019. All Rights Reserved.
|
||||
// Node module: loopback
|
||||
// This file is licensed under the MIT License.
|
||||
// License text available at https://opensource.org/licenses/MIT
|
||||
|
@ -8,9 +8,9 @@
|
|||
*/
|
||||
|
||||
'use strict';
|
||||
var g = require('../../lib/globalize');
|
||||
var loopback = require('../../lib/loopback');
|
||||
var async = require('async');
|
||||
const g = require('../../lib/globalize');
|
||||
const loopback = require('../../lib/loopback');
|
||||
const async = require('async');
|
||||
|
||||
/*!
|
||||
* Export the middleware.
|
||||
|
@ -30,26 +30,27 @@ module.exports = rest;
|
|||
*/
|
||||
|
||||
function rest() {
|
||||
var handlers; // Cached handlers
|
||||
let handlers; // Cached handlers
|
||||
|
||||
return function restApiHandler(req, res, next) {
|
||||
var app = req.app;
|
||||
var registry = app.registry;
|
||||
const app = req.app;
|
||||
const registry = app.registry;
|
||||
|
||||
if (!handlers) {
|
||||
handlers = [];
|
||||
var remotingOptions = app.get('remoting') || {};
|
||||
const remotingOptions = app.get('remoting') || {};
|
||||
|
||||
var contextOptions = remotingOptions.context;
|
||||
const contextOptions = remotingOptions.context;
|
||||
if (contextOptions !== undefined && contextOptions !== false) {
|
||||
throw new Error(g.f(
|
||||
'%s was removed in version 3.0. See %s for more details.',
|
||||
'remoting.context option',
|
||||
'http://loopback.io/doc/en/lb2/Using-current-context.html'));
|
||||
'http://loopback.io/doc/en/lb2/Using-current-context.html',
|
||||
));
|
||||
}
|
||||
|
||||
if (app.isAuthEnabled) {
|
||||
var AccessToken = registry.getModelByType('AccessToken');
|
||||
const AccessToken = registry.getModelByType('AccessToken');
|
||||
handlers.push(loopback.token({model: AccessToken, app: app}));
|
||||
}
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// Copyright IBM Corp. 2014. All Rights Reserved.
|
||||
// Copyright IBM Corp. 2014,2018. All Rights Reserved.
|
||||
// Node module: loopback
|
||||
// This file is licensed under the MIT License.
|
||||
// License text available at https://opensource.org/licenses/MIT
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// Copyright IBM Corp. 2014,2016. All Rights Reserved.
|
||||
// Copyright IBM Corp. 2014,2019. All Rights Reserved.
|
||||
// Node module: loopback
|
||||
// This file is licensed under the MIT License.
|
||||
// License text available at https://opensource.org/licenses/MIT
|
||||
|
@ -24,7 +24,7 @@ module.exports = status;
|
|||
* @header loopback.status()
|
||||
*/
|
||||
function status() {
|
||||
var started = new Date();
|
||||
const started = new Date();
|
||||
|
||||
return function(req, res) {
|
||||
res.send({
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// Copyright IBM Corp. 2014,2016. All Rights Reserved.
|
||||
// Copyright IBM Corp. 2014,2019. All Rights Reserved.
|
||||
// Node module: loopback
|
||||
// This file is licensed under the MIT License.
|
||||
// License text available at https://opensource.org/licenses/MIT
|
||||
|
@ -8,10 +8,10 @@
|
|||
*/
|
||||
|
||||
'use strict';
|
||||
var g = require('../../lib/globalize');
|
||||
var loopback = require('../../lib/loopback');
|
||||
var assert = require('assert');
|
||||
var debug = require('debug')('loopback:middleware:token');
|
||||
const g = require('../../lib/globalize');
|
||||
const loopback = require('../../lib/loopback');
|
||||
const assert = require('assert');
|
||||
const debug = require('debug')('loopback:middleware:token');
|
||||
|
||||
/*!
|
||||
* Export the middleware.
|
||||
|
@ -28,9 +28,9 @@ function rewriteUserLiteral(req, currentUserLiteral, next) {
|
|||
|
||||
if (req.accessToken && req.accessToken.userId) {
|
||||
// Replace /me/ with /current-user-id/
|
||||
var urlBeforeRewrite = req.url;
|
||||
const urlBeforeRewrite = req.url;
|
||||
req.url = req.url.replace(literalRegExp,
|
||||
'/' + req.accessToken.userId + '$1');
|
||||
'/' + req.accessToken.userId + '$1');
|
||||
|
||||
if (req.url !== urlBeforeRewrite) {
|
||||
debug('req.url has been rewritten from %s to %s', urlBeforeRewrite,
|
||||
|
@ -40,9 +40,10 @@ function rewriteUserLiteral(req, currentUserLiteral, next) {
|
|||
debug(
|
||||
'URL %s matches current-user literal %s,' +
|
||||
' but no (valid) access token was provided.',
|
||||
req.url, currentUserLiteral);
|
||||
req.url, currentUserLiteral,
|
||||
);
|
||||
|
||||
var e = new Error(g.f('Authorization Required'));
|
||||
const e = new Error(g.f('Authorization Required'));
|
||||
e.status = e.statusCode = 401;
|
||||
e.code = 'AUTHORIZATION_REQUIRED';
|
||||
return next(e);
|
||||
|
@ -96,9 +97,9 @@ function escapeRegExp(str) {
|
|||
|
||||
function token(options) {
|
||||
options = options || {};
|
||||
var TokenModel;
|
||||
let TokenModel;
|
||||
|
||||
var currentUserLiteral = options.currentUserLiteral;
|
||||
let currentUserLiteral = options.currentUserLiteral;
|
||||
if (currentUserLiteral && (typeof currentUserLiteral !== 'string')) {
|
||||
debug('Set currentUserLiteral to \'me\' as the value is not a string.');
|
||||
currentUserLiteral = 'me';
|
||||
|
@ -110,12 +111,12 @@ function token(options) {
|
|||
if (options.bearerTokenBase64Encoded === undefined) {
|
||||
options.bearerTokenBase64Encoded = true;
|
||||
}
|
||||
var enableDoublecheck = !!options.enableDoublecheck;
|
||||
var overwriteExistingToken = !!options.overwriteExistingToken;
|
||||
const enableDoublecheck = !!options.enableDoublecheck;
|
||||
const overwriteExistingToken = !!options.overwriteExistingToken;
|
||||
|
||||
return function(req, res, next) {
|
||||
var app = req.app;
|
||||
var registry = app.registry;
|
||||
const app = req.app;
|
||||
const registry = app.registry;
|
||||
if (!TokenModel) {
|
||||
TokenModel = registry.getModel(options.model || 'AccessToken');
|
||||
}
|
||||
|
@ -140,7 +141,7 @@ function token(options) {
|
|||
TokenModel.findForRequest(req, options, function(err, token) {
|
||||
req.accessToken = token || null;
|
||||
|
||||
var ctx = req.loopbackContext;
|
||||
const ctx = req.loopbackContext;
|
||||
if (ctx && ctx.active) ctx.set('accessToken', token);
|
||||
|
||||
if (err) return next(err);
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// Copyright IBM Corp. 2014. All Rights Reserved.
|
||||
// Copyright IBM Corp. 2014,2019. All Rights Reserved.
|
||||
// Node module: loopback
|
||||
// This file is licensed under the MIT License.
|
||||
// License text available at https://opensource.org/licenses/MIT
|
||||
|
@ -18,7 +18,7 @@ module.exports = urlNotFound;
|
|||
*/
|
||||
function urlNotFound() {
|
||||
return function raiseUrlNotFoundError(req, res, next) {
|
||||
var error = new Error('Cannot ' + req.method + ' ' + req.url);
|
||||
const error = new Error('Cannot ' + req.method + ' ' + req.url);
|
||||
error.status = 404;
|
||||
next(error);
|
||||
};
|
||||
|
|
|
@ -1,18 +1,18 @@
|
|||
// Copyright IBM Corp. 2013,2016. All Rights Reserved.
|
||||
// Copyright IBM Corp. 2013,2019. All Rights Reserved.
|
||||
// Node module: loopback
|
||||
// This file is licensed under the MIT License.
|
||||
// License text available at https://opensource.org/licenses/MIT
|
||||
|
||||
'use strict';
|
||||
var loopback = require('../');
|
||||
var lt = require('./helpers/loopback-testing-helper');
|
||||
var path = require('path');
|
||||
var ACCESS_CONTROL_APP = path.join(__dirname, 'fixtures', 'access-control');
|
||||
var app = require(path.join(ACCESS_CONTROL_APP, 'server/server.js'));
|
||||
var assert = require('assert');
|
||||
var USER = {email: 'test@test.test', password: 'test'};
|
||||
var CURRENT_USER = {email: 'current@test.test', password: 'test'};
|
||||
var debug = require('debug')('loopback:test:access-control.integration');
|
||||
const loopback = require('../');
|
||||
const lt = require('./helpers/loopback-testing-helper');
|
||||
const path = require('path');
|
||||
const ACCESS_CONTROL_APP = path.join(__dirname, 'fixtures', 'access-control');
|
||||
const app = require(path.join(ACCESS_CONTROL_APP, 'server/server.js'));
|
||||
const assert = require('assert');
|
||||
const USER = {email: 'test@test.test', password: 'test'};
|
||||
const CURRENT_USER = {email: 'current@test.test', password: 'test'};
|
||||
const debug = require('debug')('loopback:test:access-control.integration');
|
||||
|
||||
describe('access control - integration', function() {
|
||||
lt.beforeEach.withApp(app);
|
||||
|
@ -75,10 +75,12 @@ describe('access control - integration', function() {
|
|||
lt.it.shouldBeDeniedWhenCalledByUser(CURRENT_USER, 'GET', urlForUser);
|
||||
|
||||
lt.it.shouldBeAllowedWhenCalledAnonymously(
|
||||
'POST', '/api/users', newUserData());
|
||||
'POST', '/api/users', newUserData(),
|
||||
);
|
||||
|
||||
lt.it.shouldBeAllowedWhenCalledByUser(
|
||||
CURRENT_USER, 'POST', '/api/users', newUserData());
|
||||
CURRENT_USER, 'POST', '/api/users', newUserData(),
|
||||
);
|
||||
|
||||
lt.it.shouldBeAllowedWhenCalledByUser(CURRENT_USER, 'POST', '/api/users/logout');
|
||||
|
||||
|
@ -108,7 +110,7 @@ describe('access control - integration', function() {
|
|||
this.res.statusCode,
|
||||
this.res.headers,
|
||||
this.res.text);
|
||||
var user = this.res.body;
|
||||
const user = this.res.body;
|
||||
assert.equal(user.password, undefined);
|
||||
});
|
||||
});
|
||||
|
@ -135,7 +137,7 @@ describe('access control - integration', function() {
|
|||
return '/api/users/' + this.randomUser.id;
|
||||
}
|
||||
|
||||
var userCounter;
|
||||
var userCounter; // eslint-disable-line no-var
|
||||
function newUserData() {
|
||||
userCounter = userCounter ? ++userCounter : 1;
|
||||
|
||||
|
@ -147,14 +149,14 @@ describe('access control - integration', function() {
|
|||
});
|
||||
|
||||
describe('/banks', function() {
|
||||
var SPECIAL_USER = {email: 'special@test.test', password: 'test'};
|
||||
const SPECIAL_USER = {email: 'special@test.test', password: 'test'};
|
||||
|
||||
// define dynamic role that would only grant access when the authenticated user's email is equal to
|
||||
// SPECIAL_USER's email
|
||||
|
||||
before(function() {
|
||||
var roleModel = app.registry.getModel('Role');
|
||||
var userModel = app.registry.getModel('user');
|
||||
const roleModel = app.registry.getModel('Role');
|
||||
const userModel = app.registry.getModel('user');
|
||||
|
||||
roleModel.registerResolver('$dynamic-role', function(role, context, callback) {
|
||||
if (!(context && context.accessToken && context.accessToken.userId)) {
|
||||
|
@ -162,7 +164,7 @@ describe('access control - integration', function() {
|
|||
if (callback) callback(null, false);
|
||||
});
|
||||
}
|
||||
var accessToken = context.accessToken;
|
||||
const accessToken = context.accessToken;
|
||||
userModel.findById(accessToken.userId, function(err, user) {
|
||||
if (err) {
|
||||
return callback(err, false);
|
||||
|
@ -208,9 +210,9 @@ describe('access control - integration', function() {
|
|||
});
|
||||
|
||||
describe('/accounts with replaceOnPUT true', function() {
|
||||
var count = 0;
|
||||
let count = 0;
|
||||
before(function() {
|
||||
var roleModel = loopback.getModelByType(loopback.Role);
|
||||
const roleModel = loopback.getModelByType(loopback.Role);
|
||||
roleModel.registerResolver('$dummy', function(role, context, callback) {
|
||||
process.nextTick(function() {
|
||||
if (context.remotingContext) {
|
||||
|
@ -248,9 +250,9 @@ describe('access control - integration', function() {
|
|||
lt.it.shouldBeDeniedWhenCalledByUser(CURRENT_USER, 'PATCH', urlForAccount);
|
||||
|
||||
lt.describe.whenLoggedInAsUser(CURRENT_USER, function() {
|
||||
var actId;
|
||||
let actId;
|
||||
beforeEach(function(done) {
|
||||
var self = this;
|
||||
const self = this;
|
||||
// Create an account under the given user
|
||||
app.models.accountWithReplaceOnPUTtrue.create({
|
||||
userId: self.user.id,
|
||||
|
@ -312,9 +314,9 @@ describe('access control - integration', function() {
|
|||
lt.it.shouldBeDeniedWhenCalledByUser(CURRENT_USER, 'PATCH', urlForAccount);
|
||||
|
||||
lt.describe.whenLoggedInAsUser(CURRENT_USER, function() {
|
||||
var actId;
|
||||
let actId;
|
||||
beforeEach(function(done) {
|
||||
var self = this;
|
||||
const self = this;
|
||||
// Create an account under the given user
|
||||
app.models.accountWithReplaceOnPUTfalse.create({
|
||||
userId: self.user.id,
|
||||
|
|
|
@ -1,23 +1,23 @@
|
|||
// Copyright IBM Corp. 2013,2016. All Rights Reserved.
|
||||
// Copyright IBM Corp. 2013,2019. All Rights Reserved.
|
||||
// Node module: loopback
|
||||
// This file is licensed under the MIT License.
|
||||
// License text available at https://opensource.org/licenses/MIT
|
||||
|
||||
'use strict';
|
||||
var assert = require('assert');
|
||||
var expect = require('./helpers/expect');
|
||||
var cookieParser = require('cookie-parser');
|
||||
var LoopBackContext = require('loopback-context');
|
||||
var contextMiddleware = require('loopback-context').perRequest;
|
||||
var loopback = require('../');
|
||||
var extend = require('util')._extend;
|
||||
var session = require('express-session');
|
||||
var request = require('supertest');
|
||||
const assert = require('assert');
|
||||
const expect = require('./helpers/expect');
|
||||
const cookieParser = require('cookie-parser');
|
||||
const LoopBackContext = require('loopback-context');
|
||||
const contextMiddleware = require('loopback-context').perRequest;
|
||||
const loopback = require('../');
|
||||
const extend = require('util')._extend;
|
||||
const session = require('express-session');
|
||||
const request = require('supertest');
|
||||
|
||||
var Token, ACL, User, TestModel;
|
||||
let Token, ACL, User, TestModel;
|
||||
|
||||
describe('loopback.token(options)', function() {
|
||||
var app;
|
||||
let app;
|
||||
beforeEach(function(done) {
|
||||
app = loopback({localRegistry: true, loadBuiltinModels: true});
|
||||
app.dataSource('db', {connector: 'memory'});
|
||||
|
@ -52,7 +52,7 @@ describe('loopback.token(options)', function() {
|
|||
});
|
||||
|
||||
it('defaults to built-in AccessToken model', function() {
|
||||
var BuiltInToken = app.registry.getModel('AccessToken');
|
||||
const BuiltInToken = app.registry.getModel('AccessToken');
|
||||
app.model(BuiltInToken, {dataSource: 'db'});
|
||||
|
||||
app.enableAuth({dataSource: 'db'});
|
||||
|
@ -139,42 +139,43 @@ describe('loopback.token(options)', function() {
|
|||
});
|
||||
|
||||
it('does not search default keys when searchDefaultTokenKeys is false',
|
||||
function(done) {
|
||||
var tokenId = this.token.id;
|
||||
var app = createTestApp(
|
||||
this.token,
|
||||
{token: {searchDefaultTokenKeys: false}},
|
||||
done);
|
||||
var agent = request.agent(app);
|
||||
function(done) {
|
||||
const tokenId = this.token.id;
|
||||
const app = createTestApp(
|
||||
this.token,
|
||||
{token: {searchDefaultTokenKeys: false}},
|
||||
done,
|
||||
);
|
||||
const agent = request.agent(app);
|
||||
|
||||
// Set the token cookie
|
||||
agent.get('/token').expect(200).end(function(err, res) {
|
||||
if (err) return done(err);
|
||||
// Set the token cookie
|
||||
agent.get('/token').expect(200).end(function(err, res) {
|
||||
if (err) return done(err);
|
||||
|
||||
// Make a request that sets the token in all places searched by default
|
||||
agent.get('/check-access?access_token=' + tokenId)
|
||||
.set('X-Access-Token', tokenId)
|
||||
.set('authorization', tokenId)
|
||||
// Make a request that sets the token in all places searched by default
|
||||
agent.get('/check-access?access_token=' + tokenId)
|
||||
.set('X-Access-Token', tokenId)
|
||||
.set('authorization', tokenId)
|
||||
// Expect 401 because there is no (non-default) place configured where
|
||||
// the middleware should load the token from
|
||||
.expect(401)
|
||||
.end(done);
|
||||
.expect(401)
|
||||
.end(done);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('populates req.token from an authorization header with bearer token with base64',
|
||||
function(done) {
|
||||
var token = this.token.id;
|
||||
token = 'Bearer ' + new Buffer(token).toString('base64');
|
||||
createTestAppAndRequest(this.token, done)
|
||||
.get('/')
|
||||
.set('authorization', token)
|
||||
.expect(200)
|
||||
.end(done);
|
||||
});
|
||||
function(done) {
|
||||
let token = this.token.id;
|
||||
token = 'Bearer ' + new Buffer(token).toString('base64');
|
||||
createTestAppAndRequest(this.token, done)
|
||||
.get('/')
|
||||
.set('authorization', token)
|
||||
.expect(200)
|
||||
.end(done);
|
||||
});
|
||||
|
||||
it('populates req.token from an authorization header with bearer token', function(done) {
|
||||
var token = this.token.id;
|
||||
let token = this.token.id;
|
||||
token = 'Bearer ' + token;
|
||||
createTestAppAndRequest(this.token, {token: {bearerTokenBase64Encoded: false}}, done)
|
||||
.get('/')
|
||||
|
@ -185,7 +186,7 @@ describe('loopback.token(options)', function() {
|
|||
|
||||
describe('populating req.token from HTTP Basic Auth formatted authorization header', function() {
|
||||
it('parses "standalone-token"', function(done) {
|
||||
var token = this.token.id;
|
||||
let token = this.token.id;
|
||||
token = 'Basic ' + new Buffer(token).toString('base64');
|
||||
createTestAppAndRequest(this.token, done)
|
||||
.get('/')
|
||||
|
@ -195,7 +196,7 @@ describe('loopback.token(options)', function() {
|
|||
});
|
||||
|
||||
it('parses "token-and-empty-password:"', function(done) {
|
||||
var token = this.token.id + ':';
|
||||
let token = this.token.id + ':';
|
||||
token = 'Basic ' + new Buffer(token).toString('base64');
|
||||
createTestAppAndRequest(this.token, done)
|
||||
.get('/')
|
||||
|
@ -205,7 +206,7 @@ describe('loopback.token(options)', function() {
|
|||
});
|
||||
|
||||
it('parses "ignored-user:token-is-password"', function(done) {
|
||||
var token = 'username:' + this.token.id;
|
||||
let token = 'username:' + this.token.id;
|
||||
token = 'Basic ' + new Buffer(token).toString('base64');
|
||||
createTestAppAndRequest(this.token, done)
|
||||
.get('/')
|
||||
|
@ -215,7 +216,7 @@ describe('loopback.token(options)', function() {
|
|||
});
|
||||
|
||||
it('parses "token-is-username:ignored-password"', function(done) {
|
||||
var token = this.token.id + ':password';
|
||||
let token = this.token.id + ':password';
|
||||
token = 'Basic ' + new Buffer(token).toString('base64');
|
||||
createTestAppAndRequest(this.token, done)
|
||||
.get('/')
|
||||
|
@ -226,7 +227,7 @@ describe('loopback.token(options)', function() {
|
|||
});
|
||||
|
||||
it('populates req.token from a secure cookie', function(done) {
|
||||
var app = createTestApp(this.token, done);
|
||||
const app = createTestApp(this.token, done);
|
||||
|
||||
request(app)
|
||||
.get('/token')
|
||||
|
@ -239,8 +240,8 @@ describe('loopback.token(options)', function() {
|
|||
});
|
||||
|
||||
it('populates req.token from a header or a secure cookie', function(done) {
|
||||
var app = createTestApp(this.token, done);
|
||||
var id = this.token.id;
|
||||
const app = createTestApp(this.token, done);
|
||||
const id = this.token.id;
|
||||
request(app)
|
||||
.get('/token')
|
||||
.end(function(err, res) {
|
||||
|
@ -254,9 +255,9 @@ describe('loopback.token(options)', function() {
|
|||
|
||||
it('rewrites url for the current user literal at the end without query',
|
||||
function(done) {
|
||||
var app = createTestApp(this.token, done);
|
||||
var id = this.token.id;
|
||||
var userId = this.token.userId;
|
||||
const app = createTestApp(this.token, done);
|
||||
const id = this.token.id;
|
||||
const userId = this.token.userId;
|
||||
request(app)
|
||||
.get('/users/me')
|
||||
.set('authorization', id)
|
||||
|
@ -270,9 +271,9 @@ describe('loopback.token(options)', function() {
|
|||
|
||||
it('rewrites url for the current user literal at the end with query',
|
||||
function(done) {
|
||||
var app = createTestApp(this.token, done);
|
||||
var id = this.token.id;
|
||||
var userId = this.token.userId;
|
||||
const app = createTestApp(this.token, done);
|
||||
const id = this.token.id;
|
||||
const userId = this.token.userId;
|
||||
request(app)
|
||||
.get('/users/me?state=1')
|
||||
.set('authorization', id)
|
||||
|
@ -286,9 +287,9 @@ describe('loopback.token(options)', function() {
|
|||
|
||||
it('rewrites url for the current user literal in the middle',
|
||||
function(done) {
|
||||
var app = createTestApp(this.token, done);
|
||||
var id = this.token.id;
|
||||
var userId = this.token.userId;
|
||||
const app = createTestApp(this.token, done);
|
||||
const id = this.token.id;
|
||||
const userId = this.token.userId;
|
||||
request(app)
|
||||
.get('/users/me/1')
|
||||
.set('authorization', id)
|
||||
|
@ -302,7 +303,7 @@ describe('loopback.token(options)', function() {
|
|||
|
||||
it('generates a 401 on a current user literal route without an authToken',
|
||||
function(done) {
|
||||
var app = createTestApp(null, done);
|
||||
const app = createTestApp(null, done);
|
||||
request(app)
|
||||
.get('/users/me')
|
||||
.set('authorization', null)
|
||||
|
@ -310,9 +311,19 @@ describe('loopback.token(options)', function() {
|
|||
.end(done);
|
||||
});
|
||||
|
||||
it('generates a 401 on a current user literal route with empty authToken',
|
||||
function(done) {
|
||||
const app = createTestApp(null, done);
|
||||
request(app)
|
||||
.get('/users/me')
|
||||
.set('authorization', '')
|
||||
.expect(401)
|
||||
.end(done);
|
||||
});
|
||||
|
||||
it('generates a 401 on a current user literal route with invalid authToken',
|
||||
function(done) {
|
||||
var app = createTestApp(this.token, done);
|
||||
const app = createTestApp(this.token, done);
|
||||
request(app)
|
||||
.get('/users/me')
|
||||
.set('Authorization', 'invald-token-id')
|
||||
|
@ -321,7 +332,7 @@ describe('loopback.token(options)', function() {
|
|||
});
|
||||
|
||||
it('skips when req.token is already present', function(done) {
|
||||
var tokenStub = {id: 'stub id'};
|
||||
const tokenStub = {id: 'stub id'};
|
||||
app.use(function(req, res, next) {
|
||||
req.accessToken = tokenStub;
|
||||
|
||||
|
@ -346,34 +357,34 @@ describe('loopback.token(options)', function() {
|
|||
|
||||
describe('loading multiple instances of token middleware', function() {
|
||||
it('skips when req.token is already present and no further options are set',
|
||||
function(done) {
|
||||
var tokenStub = {id: 'stub id'};
|
||||
app.use(function(req, res, next) {
|
||||
req.accessToken = tokenStub;
|
||||
function(done) {
|
||||
const tokenStub = {id: 'stub id'};
|
||||
app.use(function(req, res, next) {
|
||||
req.accessToken = tokenStub;
|
||||
|
||||
next();
|
||||
});
|
||||
app.use(loopback.token({model: Token}));
|
||||
app.get('/', function(req, res, next) {
|
||||
res.send(req.accessToken);
|
||||
});
|
||||
|
||||
request(app).get('/')
|
||||
.set('Authorization', this.token.id)
|
||||
.expect(200)
|
||||
.end(function(err, res) {
|
||||
if (err) return done(err);
|
||||
|
||||
expect(res.body).to.eql(tokenStub);
|
||||
|
||||
done();
|
||||
next();
|
||||
});
|
||||
});
|
||||
app.use(loopback.token({model: Token}));
|
||||
app.get('/', function(req, res, next) {
|
||||
res.send(req.accessToken);
|
||||
});
|
||||
|
||||
request(app).get('/')
|
||||
.set('Authorization', this.token.id)
|
||||
.expect(200)
|
||||
.end(function(err, res) {
|
||||
if (err) return done(err);
|
||||
|
||||
expect(res.body).to.eql(tokenStub);
|
||||
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('does not overwrite valid existing token (has "id" property) ' +
|
||||
' when overwriteExistingToken is falsy',
|
||||
function(done) {
|
||||
var tokenStub = {id: 'stub id'};
|
||||
const tokenStub = {id: 'stub id'};
|
||||
app.use(function(req, res, next) {
|
||||
req.accessToken = tokenStub;
|
||||
|
||||
|
@ -402,7 +413,7 @@ describe('loopback.token(options)', function() {
|
|||
it('overwrites invalid existing token (is !== undefined and has no "id" property) ' +
|
||||
' when enableDoublecheck is true',
|
||||
function(done) {
|
||||
var token = this.token;
|
||||
const token = this.token;
|
||||
app.use(function(req, res, next) {
|
||||
req.accessToken = null;
|
||||
next();
|
||||
|
@ -435,8 +446,8 @@ describe('loopback.token(options)', function() {
|
|||
it('overwrites existing token when enableDoublecheck ' +
|
||||
'and overwriteExistingToken options are truthy',
|
||||
function(done) {
|
||||
var token = this.token;
|
||||
var tokenStub = {id: 'stub id'};
|
||||
const token = this.token;
|
||||
const tokenStub = {id: 'stub id'};
|
||||
app.use(function(req, res, next) {
|
||||
req.accessToken = tokenStub;
|
||||
|
||||
|
@ -509,29 +520,29 @@ describe('AccessToken', function() {
|
|||
});
|
||||
|
||||
it('allows eternal tokens when enabled by User.allowEternalTokens',
|
||||
function(done) {
|
||||
var Token = givenLocalTokenModel();
|
||||
function(done) {
|
||||
const Token = givenLocalTokenModel();
|
||||
|
||||
// Overwrite User settings - enable eternal tokens
|
||||
Token.app.models.User.settings.allowEternalTokens = true;
|
||||
// Overwrite User settings - enable eternal tokens
|
||||
Token.app.models.User.settings.allowEternalTokens = true;
|
||||
|
||||
Token.create({userId: '123', ttl: -1}, function(err, token) {
|
||||
if (err) return done(err);
|
||||
token.validate(function(err, isValid) {
|
||||
Token.create({userId: '123', ttl: -1}, function(err, token) {
|
||||
if (err) return done(err);
|
||||
expect(isValid, 'isValid').to.equal(true);
|
||||
done();
|
||||
token.validate(function(err, isValid) {
|
||||
if (err) return done(err);
|
||||
expect(isValid, 'isValid').to.equal(true);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('.findForRequest()', function() {
|
||||
beforeEach(createTestingToken);
|
||||
|
||||
it('supports two-arg variant with no options', function(done) {
|
||||
var expectedTokenId = this.token.id;
|
||||
var req = mockRequest({
|
||||
const expectedTokenId = this.token.id;
|
||||
const req = mockRequest({
|
||||
headers: {'authorization': expectedTokenId},
|
||||
});
|
||||
|
||||
|
@ -545,14 +556,14 @@ describe('AccessToken', function() {
|
|||
});
|
||||
|
||||
it('allows getIdForRequest() to be overridden', function(done) {
|
||||
var expectedTokenId = this.token.id;
|
||||
var current = Token.getIdForRequest;
|
||||
var called = false;
|
||||
const expectedTokenId = this.token.id;
|
||||
const current = Token.getIdForRequest;
|
||||
let called = false;
|
||||
Token.getIdForRequest = function(req, options) {
|
||||
called = true;
|
||||
return expectedTokenId;
|
||||
};
|
||||
var req = mockRequest({
|
||||
const req = mockRequest({
|
||||
headers: {'authorization': 'dummy'},
|
||||
});
|
||||
|
||||
|
@ -568,16 +579,16 @@ describe('AccessToken', function() {
|
|||
});
|
||||
|
||||
it('allows resolve() to be overridden', function(done) {
|
||||
var expectedTokenId = this.token.id;
|
||||
var current = Token.resolve;
|
||||
var called = false;
|
||||
const expectedTokenId = this.token.id;
|
||||
const current = Token.resolve;
|
||||
let called = false;
|
||||
Token.resolve = function(id, cb) {
|
||||
called = true;
|
||||
process.nextTick(function() {
|
||||
cb(null, {id: expectedTokenId});
|
||||
});
|
||||
};
|
||||
var req = mockRequest({
|
||||
const req = mockRequest({
|
||||
headers: {'authorization': expectedTokenId},
|
||||
});
|
||||
|
||||
|
@ -604,13 +615,14 @@ describe('AccessToken', function() {
|
|||
param: function(name) { return this._params[name]; },
|
||||
header: function(name) { return this.headers[name]; },
|
||||
},
|
||||
opts);
|
||||
opts,
|
||||
);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
describe('app.enableAuth()', function() {
|
||||
var app;
|
||||
let app;
|
||||
beforeEach(function setupAuthWithModels() {
|
||||
app = loopback({localRegistry: true, loadBuiltinModels: true});
|
||||
app.dataSource('db', {connector: 'memory'});
|
||||
|
@ -641,7 +653,7 @@ describe('app.enableAuth()', function() {
|
|||
return done(err);
|
||||
}
|
||||
|
||||
var errorResponse = res.body.error;
|
||||
const errorResponse = res.body.error;
|
||||
assert(errorResponse);
|
||||
assert.equal(errorResponse.code, 'AUTHORIZATION_REQUIRED');
|
||||
|
||||
|
@ -649,7 +661,7 @@ describe('app.enableAuth()', function() {
|
|||
});
|
||||
});
|
||||
|
||||
it('prevent remote call with app setting status on denied ACL', function(done) {
|
||||
it('denies remote call with app setting status 403', function(done) {
|
||||
createTestAppAndRequest(this.token, {app: {aclErrorStatus: 403}}, done)
|
||||
.del('/tests/123')
|
||||
.expect(403)
|
||||
|
@ -659,7 +671,7 @@ describe('app.enableAuth()', function() {
|
|||
return done(err);
|
||||
}
|
||||
|
||||
var errorResponse = res.body.error;
|
||||
const errorResponse = res.body.error;
|
||||
assert(errorResponse);
|
||||
assert.equal(errorResponse.code, 'ACCESS_DENIED');
|
||||
|
||||
|
@ -667,7 +679,7 @@ describe('app.enableAuth()', function() {
|
|||
});
|
||||
});
|
||||
|
||||
it('prevent remote call with app setting status on denied ACL', function(done) {
|
||||
it('denies remote call with app setting status 404', function(done) {
|
||||
createTestAppAndRequest(this.token, {model: {aclErrorStatus: 404}}, done)
|
||||
.del('/tests/123')
|
||||
.expect(404)
|
||||
|
@ -677,7 +689,7 @@ describe('app.enableAuth()', function() {
|
|||
return done(err);
|
||||
}
|
||||
|
||||
var errorResponse = res.body.error;
|
||||
const errorResponse = res.body.error;
|
||||
assert(errorResponse);
|
||||
assert.equal(errorResponse.code, 'MODEL_NOT_FOUND');
|
||||
|
||||
|
@ -695,7 +707,7 @@ describe('app.enableAuth()', function() {
|
|||
return done(err);
|
||||
}
|
||||
|
||||
var errorResponse = res.body.error;
|
||||
const errorResponse = res.body.error;
|
||||
assert(errorResponse);
|
||||
assert.equal(errorResponse.code, 'AUTHORIZATION_REQUIRED');
|
||||
|
||||
|
@ -704,9 +716,9 @@ describe('app.enableAuth()', function() {
|
|||
});
|
||||
|
||||
it('stores token in the context', function(done) {
|
||||
var TestModel = app.registry.createModel('TestModel', {base: 'Model'});
|
||||
const TestModel = app.registry.createModel('TestModel', {base: 'Model'});
|
||||
TestModel.getToken = function(cb) {
|
||||
var ctx = LoopBackContext.getCurrentContext();
|
||||
const ctx = LoopBackContext.getCurrentContext();
|
||||
cb(null, ctx && ctx.get('accessToken') || null);
|
||||
};
|
||||
TestModel.remoteMethod('getToken', {
|
||||
|
@ -721,7 +733,7 @@ describe('app.enableAuth()', function() {
|
|||
app.use(loopback.token({model: Token}));
|
||||
app.use(loopback.rest());
|
||||
|
||||
var token = this.token;
|
||||
const token = this.token;
|
||||
request(app)
|
||||
.get('/TestModels/token?_format=json')
|
||||
.set('authorization', token.id)
|
||||
|
@ -760,7 +772,7 @@ describe('app.enableAuth()', function() {
|
|||
});
|
||||
|
||||
function createTestingToken(done) {
|
||||
var test = this;
|
||||
const test = this;
|
||||
Token.create({userId: '123'}, function(err, token) {
|
||||
if (err) return done(err);
|
||||
|
||||
|
@ -771,7 +783,7 @@ function createTestingToken(done) {
|
|||
}
|
||||
|
||||
function createTestAppAndRequest(testToken, settings, done) {
|
||||
var app = createTestApp(testToken, settings, done);
|
||||
const app = createTestApp(testToken, settings, done);
|
||||
return request(app);
|
||||
}
|
||||
|
||||
|
@ -781,14 +793,14 @@ function createTestApp(testToken, settings, done) {
|
|||
settings = {};
|
||||
}
|
||||
|
||||
var appSettings = settings.app || {};
|
||||
var modelSettings = settings.model || {};
|
||||
var tokenSettings = extend({
|
||||
const appSettings = settings.app || {};
|
||||
const modelSettings = settings.model || {};
|
||||
const tokenSettings = extend({
|
||||
model: Token,
|
||||
currentUserLiteral: 'me',
|
||||
}, settings.token);
|
||||
|
||||
var app = loopback({localRegistry: true, loadBuiltinModels: true});
|
||||
const app = loopback({localRegistry: true, loadBuiltinModels: true});
|
||||
app.dataSource('db', {connector: 'memory'});
|
||||
|
||||
app.use(cookieParser('secret'));
|
||||
|
@ -812,7 +824,7 @@ function createTestApp(testToken, settings, done) {
|
|||
res.status(req.accessToken ? 200 : 401).end();
|
||||
});
|
||||
app.use('/users/:uid', function(req, res) {
|
||||
var result = {userId: req.params.uid};
|
||||
const result = {userId: req.params.uid};
|
||||
if (req.query.state) {
|
||||
result.state = req.query.state;
|
||||
} else if (req.url !== '/') {
|
||||
|
@ -827,7 +839,7 @@ function createTestApp(testToken, settings, done) {
|
|||
app.set(key, appSettings[key]);
|
||||
});
|
||||
|
||||
var modelOptions = {
|
||||
const modelOptions = {
|
||||
acls: [
|
||||
{
|
||||
principalType: 'ROLE',
|
||||
|
@ -843,20 +855,20 @@ function createTestApp(testToken, settings, done) {
|
|||
modelOptions[key] = modelSettings[key];
|
||||
});
|
||||
|
||||
var TestModel = app.registry.createModel('test', {}, modelOptions);
|
||||
const TestModel = app.registry.createModel('test', {}, modelOptions);
|
||||
app.model(TestModel, {dataSource: 'db'});
|
||||
|
||||
return app;
|
||||
}
|
||||
|
||||
function givenLocalTokenModel() {
|
||||
var app = loopback({localRegistry: true, loadBuiltinModels: true});
|
||||
const app = loopback({localRegistry: true, loadBuiltinModels: true});
|
||||
app.dataSource('db', {connector: 'memory'});
|
||||
|
||||
var User = app.registry.getModel('User');
|
||||
const User = app.registry.getModel('User');
|
||||
app.model(User, {dataSource: 'db'});
|
||||
|
||||
var Token = app.registry.getModel('AccessToken');
|
||||
const Token = app.registry.getModel('AccessToken');
|
||||
app.model(Token, {dataSource: 'db'});
|
||||
|
||||
return Token;
|
||||
|
|
565
test/acl.test.js
565
test/acl.test.js
|
@ -1,34 +1,27 @@
|
|||
// Copyright IBM Corp. 2013,2016. All Rights Reserved.
|
||||
// Copyright IBM Corp. 2013,2019. All Rights Reserved.
|
||||
// Node module: loopback
|
||||
// This file is licensed under the MIT License.
|
||||
// License text available at https://opensource.org/licenses/MIT
|
||||
|
||||
'use strict';
|
||||
var assert = require('assert');
|
||||
var expect = require('./helpers/expect');
|
||||
var loopback = require('../index');
|
||||
var Scope = loopback.Scope;
|
||||
var ACL = loopback.ACL;
|
||||
var request = require('supertest');
|
||||
var Promise = require('bluebird');
|
||||
var supertest = require('supertest');
|
||||
var Role = loopback.Role;
|
||||
var RoleMapping = loopback.RoleMapping;
|
||||
var User = loopback.User;
|
||||
var testModel;
|
||||
const assert = require('assert');
|
||||
const expect = require('./helpers/expect');
|
||||
const loopback = require('../index');
|
||||
const Scope = loopback.Scope;
|
||||
const ACL = loopback.ACL;
|
||||
const request = require('supertest');
|
||||
const Promise = require('bluebird');
|
||||
const supertest = require('supertest');
|
||||
const Role = loopback.Role;
|
||||
const RoleMapping = loopback.RoleMapping;
|
||||
const User = loopback.User;
|
||||
const async = require('async');
|
||||
|
||||
// Speed up the password hashing algorithm for tests
|
||||
User.settings.saltWorkFactor = 4;
|
||||
|
||||
function checkResult(err, result) {
|
||||
// console.log(err, result);
|
||||
assert(!err);
|
||||
}
|
||||
|
||||
var ds = null;
|
||||
before(function() {
|
||||
ds = loopback.createDataSource({connector: loopback.Memory});
|
||||
});
|
||||
let ds = null;
|
||||
let testModel;
|
||||
|
||||
describe('ACL model', function() {
|
||||
it('provides DEFAULT_SCOPE constant', () => {
|
||||
|
@ -37,68 +30,64 @@ describe('ACL model', function() {
|
|||
});
|
||||
|
||||
describe('security scopes', function() {
|
||||
beforeEach(function() {
|
||||
var ds = this.ds = loopback.createDataSource({connector: loopback.Memory});
|
||||
testModel = loopback.PersistedModel.extend('testModel');
|
||||
ACL.attachTo(ds);
|
||||
Role.attachTo(ds);
|
||||
RoleMapping.attachTo(ds);
|
||||
User.attachTo(ds);
|
||||
Scope.attachTo(ds);
|
||||
testModel.attachTo(ds);
|
||||
});
|
||||
beforeEach(setupTestModels);
|
||||
|
||||
it('should allow access to models for the given scope by wildcard', function() {
|
||||
it('should allow access to models for the given scope by wildcard', function(done) {
|
||||
Scope.create({name: 'userScope', description: 'access user information'},
|
||||
function(err, scope) {
|
||||
ACL.create({
|
||||
principalType: ACL.SCOPE, principalId: scope.id,
|
||||
model: 'User', property: ACL.ALL,
|
||||
accessType: ACL.ALL, permission: ACL.ALLOW,
|
||||
}, function(err, resource) {
|
||||
Scope.checkPermission('userScope', 'User', ACL.ALL, ACL.ALL, checkResult);
|
||||
Scope.checkPermission('userScope', 'User', 'name', ACL.ALL, checkResult);
|
||||
Scope.checkPermission('userScope', 'User', 'name', ACL.READ, checkResult);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('should allow access to models for the given scope', function() {
|
||||
Scope.create({name: 'testModelScope', description: 'access testModel information'},
|
||||
function(err, scope) {
|
||||
ACL.create({
|
||||
principalType: ACL.SCOPE, principalId: scope.id,
|
||||
model: 'testModel', property: 'name',
|
||||
accessType: ACL.READ, permission: ACL.ALLOW,
|
||||
}, function(err, resource) {
|
||||
ACL.create({principalType: ACL.SCOPE, principalId: scope.id,
|
||||
model: 'testModel', property: 'name',
|
||||
accessType: ACL.WRITE, permission: ACL.DENY,
|
||||
function(err, scope) {
|
||||
ACL.create({
|
||||
principalType: ACL.SCOPE, principalId: scope.id,
|
||||
model: 'User', property: ACL.ALL,
|
||||
accessType: ACL.ALL, permission: ACL.ALLOW,
|
||||
}, function(err, resource) {
|
||||
// console.log(resource);
|
||||
Scope.checkPermission('testModelScope', 'testModel', ACL.ALL, ACL.ALL,
|
||||
function(err, perm) {
|
||||
assert(perm.permission === ACL.DENY); // because name.WRITE == DENY
|
||||
});
|
||||
Scope.checkPermission('testModelScope', 'testModel', 'name', ACL.ALL,
|
||||
function(err, perm) {
|
||||
assert(perm.permission === ACL.DENY); // because name.WRITE == DENY
|
||||
});
|
||||
Scope.checkPermission('testModelScope', 'testModel', 'name', ACL.READ,
|
||||
function(err, perm) {
|
||||
assert(perm.permission === ACL.ALLOW);
|
||||
});
|
||||
Scope.checkPermission('testModelScope', 'testModel', 'name', ACL.WRITE,
|
||||
function(err, perm) {
|
||||
assert(perm.permission === ACL.DENY);
|
||||
});
|
||||
async.parallel([
|
||||
cb => Scope.checkPermission('userScope', 'User', ACL.ALL, ACL.ALL, cb),
|
||||
cb => Scope.checkPermission('userScope', 'User', 'name', ACL.ALL, cb),
|
||||
cb => Scope.checkPermission('userScope', 'User', 'name', ACL.READ, cb),
|
||||
], (err) => {
|
||||
assert.ifError(err);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('should allow access to models for the given scope', function(done) {
|
||||
Scope.create({name: 'testModelScope', description: 'access testModel information'},
|
||||
function(err, scope) {
|
||||
ACL.create({
|
||||
principalType: ACL.SCOPE, principalId: scope.id,
|
||||
model: 'testModel', property: 'name',
|
||||
accessType: ACL.READ, permission: ACL.ALLOW,
|
||||
}, function(err, resource) {
|
||||
ACL.create({principalType: ACL.SCOPE, principalId: scope.id,
|
||||
model: 'testModel', property: 'name',
|
||||
accessType: ACL.WRITE, permission: ACL.DENY,
|
||||
}, function(err, resource) {
|
||||
async.parallel([
|
||||
cb => Scope.checkPermission('testModelScope', 'testModel', ACL.ALL, ACL.ALL, cb),
|
||||
cb => Scope.checkPermission('testModelScope', 'testModel', 'name', ACL.ALL, cb),
|
||||
cb => Scope.checkPermission('testModelScope', 'testModel', 'name', ACL.READ, cb),
|
||||
cb => Scope.checkPermission('testModelScope', 'testModel', 'name', ACL.WRITE, cb),
|
||||
], (err, perms) => {
|
||||
if (err) return done(err);
|
||||
assert.deepEqual(perms.map(p => p.permission), [
|
||||
ACL.DENY,
|
||||
ACL.DENY,
|
||||
ACL.ALLOW,
|
||||
ACL.DENY,
|
||||
]);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('security ACLs', function() {
|
||||
beforeEach(setupTestModels);
|
||||
|
||||
it('supports checkPermission() returning a promise', function() {
|
||||
return ACL.create({
|
||||
principalType: ACL.USER,
|
||||
|
@ -108,16 +97,54 @@ describe('security ACLs', function() {
|
|||
accessType: ACL.ALL,
|
||||
permission: ACL.ALLOW,
|
||||
})
|
||||
.then(function() {
|
||||
return ACL.checkPermission(ACL.USER, 'u001', 'testModel', 'name', ACL.ALL);
|
||||
})
|
||||
.then(function(access) {
|
||||
assert(access.permission === ACL.ALLOW);
|
||||
});
|
||||
.then(function() {
|
||||
return ACL.checkPermission(ACL.USER, 'u001', 'testModel', 'name', ACL.ALL);
|
||||
})
|
||||
.then(function(access) {
|
||||
assert(access.permission === ACL.ALLOW);
|
||||
});
|
||||
});
|
||||
|
||||
it('supports ACL rules with a wildcard for models', function() {
|
||||
const A_USER_ID = 'a-test-user';
|
||||
|
||||
// By default, access is allowed to all users
|
||||
return assertPermission(ACL.ALLOW, 'initial state')
|
||||
// An ACL rule applying to all models denies access to everybody
|
||||
.then(() => ACL.create({
|
||||
model: '*',
|
||||
property: '*',
|
||||
accessType: '*',
|
||||
principalType: 'ROLE',
|
||||
principalId: '$everyone',
|
||||
permission: 'DENY',
|
||||
}))
|
||||
.then(() => assertPermission(ACL.DENY, 'all denied'))
|
||||
// A rule for a specific model overrides the rule matching all models
|
||||
.then(() => ACL.create({
|
||||
model: testModel.modelName,
|
||||
property: '*',
|
||||
accessType: '*',
|
||||
principalType: ACL.USER,
|
||||
principalId: A_USER_ID,
|
||||
permission: ACL.ALLOW,
|
||||
}))
|
||||
.then(() => assertPermission(ACL.ALLOW, 'only a single model allowed'));
|
||||
|
||||
function assertPermission(expectedPermission, msg) {
|
||||
return ACL.checkAccessForContext({
|
||||
principals: [{type: ACL.USER, id: A_USER_ID}],
|
||||
model: testModel.modelName,
|
||||
accessType: ACL.ALL,
|
||||
}).then(accessContext => {
|
||||
const actual = accessContext.isAllowed() ? ACL.ALLOW : ACL.DENY;
|
||||
expect(actual, msg).to.equal(expectedPermission);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
it('supports checkAccessForContext() returning a promise', function() {
|
||||
var testModel = ds.createModel('testModel', {
|
||||
const testModel = ds.createModel('testModel', {
|
||||
acls: [
|
||||
{principalType: ACL.USER, principalId: 'u001',
|
||||
accessType: ACL.ALL, permission: ACL.ALLOW},
|
||||
|
@ -129,13 +156,13 @@ describe('security ACLs', function() {
|
|||
model: 'testModel',
|
||||
accessType: ACL.ALL,
|
||||
})
|
||||
.then(function(access) {
|
||||
assert(access.permission === ACL.ALLOW);
|
||||
});
|
||||
.then(function(access) {
|
||||
assert(access.permission === ACL.ALLOW);
|
||||
});
|
||||
});
|
||||
|
||||
it('should order ACL entries based on the matching score', function() {
|
||||
var acls = [
|
||||
let acls = [
|
||||
{
|
||||
'model': 'account',
|
||||
'accessType': '*',
|
||||
|
@ -157,7 +184,7 @@ describe('security ACLs', function() {
|
|||
'principalType': 'ROLE',
|
||||
'principalId': '$everyone',
|
||||
}];
|
||||
var req = {
|
||||
const req = {
|
||||
model: 'account',
|
||||
property: 'find',
|
||||
accessType: 'WRITE',
|
||||
|
@ -165,7 +192,7 @@ describe('security ACLs', function() {
|
|||
|
||||
acls = acls.map(function(a) { return new ACL(a); });
|
||||
|
||||
var perm = ACL.resolvePermission(acls, req);
|
||||
const perm = ACL.resolvePermission(acls, req);
|
||||
// remove the registry from AccessRequest instance to ease asserting
|
||||
delete perm.registry;
|
||||
assert.deepEqual(perm, {model: 'account',
|
||||
|
@ -186,7 +213,42 @@ describe('security ACLs', function() {
|
|||
// });
|
||||
});
|
||||
|
||||
it('should allow access to models for the given principal by wildcard', function() {
|
||||
it('should order ACL entries based on the matching score even with wildcard req', function() {
|
||||
let acls = [
|
||||
{
|
||||
'model': 'account',
|
||||
'accessType': '*',
|
||||
'permission': 'DENY',
|
||||
'principalType': 'ROLE',
|
||||
'principalId': '$everyone',
|
||||
},
|
||||
{
|
||||
'model': 'account',
|
||||
'accessType': '*',
|
||||
'permission': 'ALLOW',
|
||||
'principalType': 'ROLE',
|
||||
'principalId': '$owner',
|
||||
}];
|
||||
const req = {
|
||||
model: 'account',
|
||||
property: '*',
|
||||
accessType: 'WRITE',
|
||||
};
|
||||
|
||||
acls = acls.map(function(a) { return new ACL(a); });
|
||||
|
||||
const perm = ACL.resolvePermission(acls, req);
|
||||
// remove the registry from AccessRequest instance to ease asserting.
|
||||
// Check the above test case for more info.
|
||||
delete perm.registry;
|
||||
assert.deepEqual(perm, {model: 'account',
|
||||
property: '*',
|
||||
accessType: 'WRITE',
|
||||
permission: 'ALLOW',
|
||||
methodNames: []});
|
||||
});
|
||||
|
||||
it('should allow access to models for the given principal by wildcard', function(done) {
|
||||
// jscs:disable validateIndentation
|
||||
ACL.create({
|
||||
principalType: ACL.USER, principalId: 'u001', model: 'User', property: ACL.ALL,
|
||||
|
@ -196,18 +258,22 @@ describe('security ACLs', function() {
|
|||
principalType: ACL.USER, principalId: 'u001', model: 'User', property: ACL.ALL,
|
||||
accessType: ACL.READ, permission: ACL.DENY,
|
||||
}, function(err, acl) {
|
||||
ACL.checkPermission(ACL.USER, 'u001', 'User', 'name', ACL.READ, function(err, perm) {
|
||||
assert(perm.permission === ACL.DENY);
|
||||
});
|
||||
|
||||
ACL.checkPermission(ACL.USER, 'u001', 'User', 'name', ACL.ALL, function(err, perm) {
|
||||
assert(perm.permission === ACL.DENY);
|
||||
async.parallel([
|
||||
cb => ACL.checkPermission(ACL.USER, 'u001', 'User', 'name', ACL.READ, cb),
|
||||
cb => ACL.checkPermission(ACL.USER, 'u001', 'User', 'name', ACL.ALL, cb),
|
||||
], (err, perms) => {
|
||||
if (err) return done(err);
|
||||
assert.deepEqual(perms.map(p => p.permission), [
|
||||
ACL.DENY,
|
||||
ACL.DENY,
|
||||
]);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('should allow access to models by exception', function() {
|
||||
it('should allow access to models by exception', function(done) {
|
||||
ACL.create({
|
||||
principalType: ACL.USER, principalId: 'u001', model: 'testModel', property: ACL.ALL,
|
||||
accessType: ACL.ALL, permission: ACL.DENY,
|
||||
|
@ -220,42 +286,32 @@ describe('security ACLs', function() {
|
|||
principalType: ACL.USER, principalId: 'u002', model: 'testModel', property: ACL.ALL,
|
||||
accessType: ACL.EXECUTE, permission: ACL.ALLOW,
|
||||
}, function(err, acl) {
|
||||
ACL.checkPermission(ACL.USER, 'u001', 'testModel', 'name', ACL.READ,
|
||||
function(err, perm) {
|
||||
assert(perm.permission === ACL.ALLOW);
|
||||
});
|
||||
|
||||
ACL.checkPermission(ACL.USER, 'u001', 'testModel', ACL.ALL, ACL.READ,
|
||||
function(err, perm) {
|
||||
assert(perm.permission === ACL.ALLOW);
|
||||
});
|
||||
|
||||
ACL.checkPermission(ACL.USER, 'u001', 'testModel', 'name', ACL.WRITE,
|
||||
function(err, perm) {
|
||||
assert(perm.permission === ACL.DENY);
|
||||
});
|
||||
|
||||
ACL.checkPermission(ACL.USER, 'u001', 'testModel', 'name', ACL.ALL,
|
||||
function(err, perm) {
|
||||
assert(perm.permission === ACL.DENY);
|
||||
});
|
||||
|
||||
ACL.checkPermission(ACL.USER, 'u002', 'testModel', 'name', ACL.WRITE,
|
||||
function(err, perm) {
|
||||
assert(perm.permission === ACL.ALLOW);
|
||||
});
|
||||
|
||||
ACL.checkPermission(ACL.USER, 'u002', 'testModel', 'name', ACL.READ,
|
||||
function(err, perm) {
|
||||
assert(perm.permission === ACL.ALLOW);
|
||||
async.parallel([
|
||||
cb => ACL.checkPermission(ACL.USER, 'u001', 'testModel', 'name', ACL.READ, cb),
|
||||
cb => ACL.checkPermission(ACL.USER, 'u001', 'testModel', ACL.ALL, ACL.READ, cb),
|
||||
cb => ACL.checkPermission(ACL.USER, 'u001', 'testModel', 'name', ACL.WRITE, cb),
|
||||
cb => ACL.checkPermission(ACL.USER, 'u001', 'testModel', 'name', ACL.ALL, cb),
|
||||
cb => ACL.checkPermission(ACL.USER, 'u002', 'testModel', 'name', ACL.WRITE, cb),
|
||||
cb => ACL.checkPermission(ACL.USER, 'u002', 'testModel', 'name', ACL.READ, cb),
|
||||
], (err, perms) => {
|
||||
if (err) return done(err);
|
||||
assert.deepEqual(perms.map(p => p.permission), [
|
||||
ACL.ALLOW,
|
||||
ACL.ALLOW,
|
||||
ACL.DENY,
|
||||
ACL.DENY,
|
||||
ACL.ALLOW,
|
||||
ACL.ALLOW,
|
||||
]);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('should honor defaultPermission from the model', function() {
|
||||
var Customer = ds.createModel('Customer', {
|
||||
it('should honor defaultPermission from the model', function(done) {
|
||||
const Customer = ds.createModel('Customer', {
|
||||
name: {
|
||||
type: String,
|
||||
acls: [
|
||||
|
@ -275,22 +331,23 @@ describe('security ACLs', function() {
|
|||
// ACL default permission is to DENY for model Customer
|
||||
Customer.settings.defaultPermission = ACL.DENY;
|
||||
|
||||
ACL.checkPermission(ACL.USER, 'u001', 'Customer', 'name', ACL.WRITE,
|
||||
function(err, perm) {
|
||||
assert(perm.permission === ACL.DENY);
|
||||
});
|
||||
|
||||
ACL.checkPermission(ACL.USER, 'u001', 'Customer', 'name', ACL.READ, function(err, perm) {
|
||||
assert(perm.permission === ACL.ALLOW);
|
||||
});
|
||||
|
||||
ACL.checkPermission(ACL.USER, 'u002', 'Customer', 'name', ACL.WRITE, function(err, perm) {
|
||||
assert(perm.permission === ACL.DENY);
|
||||
async.parallel([
|
||||
cb => ACL.checkPermission(ACL.USER, 'u001', 'Customer', 'name', ACL.WRITE, cb),
|
||||
cb => ACL.checkPermission(ACL.USER, 'u001', 'Customer', 'name', ACL.READ, cb),
|
||||
cb => ACL.checkPermission(ACL.USER, 'u002', 'Customer', 'name', ACL.WRITE, cb),
|
||||
], (err, perms) => {
|
||||
if (err) return done(err);
|
||||
assert.deepEqual(perms.map(p => p.permission), [
|
||||
ACL.DENY,
|
||||
ACL.ALLOW,
|
||||
ACL.DENY,
|
||||
]);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should honor static ACLs from the model', function() {
|
||||
var Customer = ds.createModel('Customer', {
|
||||
it('should honor static ACLs from the model', function(done) {
|
||||
const Customer = ds.createModel('Customer', {
|
||||
name: {
|
||||
type: String,
|
||||
acls: [
|
||||
|
@ -317,34 +374,27 @@ describe('security ACLs', function() {
|
|||
];
|
||||
*/
|
||||
|
||||
ACL.checkPermission(ACL.USER, 'u001', 'Customer', 'name', ACL.WRITE,
|
||||
function(err, perm) {
|
||||
assert(perm.permission === ACL.DENY);
|
||||
});
|
||||
|
||||
ACL.checkPermission(ACL.USER, 'u001', 'Customer', 'name', ACL.READ,
|
||||
function(err, perm) {
|
||||
assert(perm.permission === ACL.ALLOW);
|
||||
});
|
||||
|
||||
ACL.checkPermission(ACL.USER, 'u001', 'Customer', 'name', ACL.ALL,
|
||||
function(err, perm) {
|
||||
assert(perm.permission === ACL.ALLOW);
|
||||
});
|
||||
|
||||
ACL.checkPermission(ACL.USER, 'u002', 'Customer', 'name', ACL.READ,
|
||||
function(err, perm) {
|
||||
assert(perm.permission === ACL.ALLOW);
|
||||
});
|
||||
|
||||
ACL.checkPermission(ACL.USER, 'u003', 'Customer', 'name', ACL.WRITE,
|
||||
function(err, perm) {
|
||||
assert(perm.permission === ACL.DENY);
|
||||
async.parallel([
|
||||
cb => ACL.checkPermission(ACL.USER, 'u001', 'Customer', 'name', ACL.WRITE, cb),
|
||||
cb => ACL.checkPermission(ACL.USER, 'u001', 'Customer', 'name', ACL.READ, cb),
|
||||
cb => ACL.checkPermission(ACL.USER, 'u001', 'Customer', 'name', ACL.ALL, cb),
|
||||
cb => ACL.checkPermission(ACL.USER, 'u002', 'Customer', 'name', ACL.READ, cb),
|
||||
cb => ACL.checkPermission(ACL.USER, 'u003', 'Customer', 'name', ACL.WRITE, cb),
|
||||
], (err, perms) => {
|
||||
if (err) return done(err);
|
||||
assert.deepEqual(perms.map(p => p.permission), [
|
||||
ACL.DENY,
|
||||
ACL.ALLOW,
|
||||
ACL.ALLOW,
|
||||
ACL.ALLOW,
|
||||
ACL.DENY,
|
||||
]);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should filter static ACLs by model/property', function() {
|
||||
var Model1 = ds.createModel('Model1', {
|
||||
const Model1 = ds.createModel('Model1', {
|
||||
name: {
|
||||
type: String,
|
||||
acls: [
|
||||
|
@ -365,7 +415,7 @@ describe('security ACLs', function() {
|
|||
],
|
||||
});
|
||||
|
||||
var staticACLs = ACL.getStaticACLs('Model1', 'name');
|
||||
let staticACLs = ACL.getStaticACLs('Model1', 'name');
|
||||
assert(staticACLs.length === 3);
|
||||
|
||||
staticACLs = ACL.getStaticACLs('Model1', 'findOne');
|
||||
|
@ -376,18 +426,17 @@ describe('security ACLs', function() {
|
|||
assert(staticACLs[0].property === 'findById');
|
||||
});
|
||||
|
||||
it('should check access against LDL, ACL, and Role', function() {
|
||||
// var log = console.log;
|
||||
var log = function() {};
|
||||
it('should check access against LDL, ACL, and Role', function(done) {
|
||||
const log = function() {};
|
||||
|
||||
// Create
|
||||
User.create({name: 'Raymond', email: 'x@y.com', password: 'foobar'}, function(err, user) {
|
||||
log('User: ', user.toObject());
|
||||
|
||||
var userId = user.id;
|
||||
const userId = user.id;
|
||||
|
||||
// Define a model with static ACLs
|
||||
var Customer = ds.createModel('Customer', {
|
||||
const Customer = ds.createModel('Customer', {
|
||||
name: {
|
||||
type: String,
|
||||
acls: [
|
||||
|
@ -416,39 +465,47 @@ describe('security ACLs', function() {
|
|||
log('Role: ', myRole.toObject());
|
||||
|
||||
myRole.principals.create({principalType: RoleMapping.USER, principalId: userId},
|
||||
function(err, p) {
|
||||
log('Principal added to role: ', p.toObject());
|
||||
function(err, p) {
|
||||
log('Principal added to role: ', p.toObject());
|
||||
|
||||
ACL.create({
|
||||
principalType: ACL.ROLE, principalId: 'MyRole',
|
||||
model: 'Customer', property: ACL.ALL,
|
||||
accessType: ACL.READ, permission: ACL.DENY,
|
||||
}, function(err, acl) {
|
||||
log('ACL 2: ', acl.toObject());
|
||||
ACL.create({
|
||||
principalType: ACL.ROLE, principalId: 'MyRole',
|
||||
model: 'Customer', property: ACL.ALL,
|
||||
accessType: ACL.READ, permission: ACL.DENY,
|
||||
}, function(err, acl) {
|
||||
log('ACL 2: ', acl.toObject());
|
||||
|
||||
ACL.checkAccessForContext({
|
||||
principals: [
|
||||
{type: ACL.USER, id: userId},
|
||||
],
|
||||
model: 'Customer',
|
||||
property: 'name',
|
||||
accessType: ACL.READ,
|
||||
}, function(err, access) {
|
||||
assert(!err && access.permission === ACL.ALLOW);
|
||||
});
|
||||
|
||||
ACL.checkAccessForContext({
|
||||
principals: [
|
||||
{type: ACL.ROLE, id: Role.EVERYONE},
|
||||
],
|
||||
model: 'Customer',
|
||||
property: 'name',
|
||||
accessType: ACL.READ,
|
||||
}, function(err, access) {
|
||||
assert(!err && access.permission === ACL.DENY);
|
||||
async.parallel([
|
||||
cb => {
|
||||
ACL.checkAccessForContext({
|
||||
principals: [
|
||||
{type: ACL.USER, id: userId},
|
||||
],
|
||||
model: 'Customer',
|
||||
property: 'name',
|
||||
accessType: ACL.READ,
|
||||
}, function(err, access) {
|
||||
assert.ifError(err);
|
||||
assert.equal(access.permission, ACL.ALLOW);
|
||||
cb();
|
||||
});
|
||||
},
|
||||
cb => {
|
||||
ACL.checkAccessForContext({
|
||||
principals: [
|
||||
{type: ACL.ROLE, id: Role.EVERYONE},
|
||||
],
|
||||
model: 'Customer',
|
||||
property: 'name',
|
||||
accessType: ACL.READ,
|
||||
}, function(err, access) {
|
||||
assert.ifError(err);
|
||||
assert.equal(access.permission, ACL.DENY);
|
||||
cb();
|
||||
});
|
||||
}], done);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -457,10 +514,10 @@ describe('security ACLs', function() {
|
|||
|
||||
describe('access check', function() {
|
||||
it('should occur before other remote hooks', function(done) {
|
||||
var app = loopback();
|
||||
var MyTestModel = app.registry.createModel('MyTestModel');
|
||||
var checkAccessCalled = false;
|
||||
var beforeHookCalled = false;
|
||||
const app = loopback();
|
||||
const MyTestModel = app.registry.createModel('MyTestModel');
|
||||
let checkAccessCalled = false;
|
||||
let beforeHookCalled = false;
|
||||
|
||||
app.use(loopback.rest());
|
||||
app.set('remoting', {errorHandler: {debug: true, log: false}});
|
||||
|
@ -470,9 +527,9 @@ describe('access check', function() {
|
|||
|
||||
// fake / spy on the checkAccess method
|
||||
MyTestModel.checkAccess = function() {
|
||||
var cb = arguments[arguments.length - 1];
|
||||
const cb = arguments[arguments.length - 1];
|
||||
checkAccessCalled = true;
|
||||
var allowed = true;
|
||||
const allowed = true;
|
||||
cb(null, allowed);
|
||||
};
|
||||
|
||||
|
@ -497,8 +554,8 @@ describe('access check', function() {
|
|||
});
|
||||
|
||||
describe('authorized roles propagation in RemotingContext', function() {
|
||||
var app, request, accessToken;
|
||||
var models = {};
|
||||
let app, request, accessToken;
|
||||
let models = {};
|
||||
|
||||
beforeEach(setupAppAndRequest);
|
||||
|
||||
|
@ -508,35 +565,35 @@ describe('authorized roles propagation in RemotingContext', function() {
|
|||
{permission: ACL.ALLOW, principalId: '$authenticated'},
|
||||
{permission: ACL.ALLOW, principalId: 'myRole'},
|
||||
])
|
||||
.then(makeAuthorizedHttpRequestOnMyTestModel)
|
||||
.then(function() {
|
||||
var ctx = models.MyTestModel.lastRemotingContext;
|
||||
expect(ctx.args.options.authorizedRoles).to.eql(
|
||||
{
|
||||
$everyone: true,
|
||||
$authenticated: true,
|
||||
myRole: true,
|
||||
}
|
||||
);
|
||||
});
|
||||
.then(makeAuthorizedHttpRequestOnMyTestModel)
|
||||
.then(function() {
|
||||
const ctx = models.MyTestModel.lastRemotingContext;
|
||||
expect(ctx.args.options.authorizedRoles).to.eql(
|
||||
{
|
||||
$everyone: true,
|
||||
$authenticated: true,
|
||||
myRole: true,
|
||||
},
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
it('does not contain any denied role even if query is allowed', function() {
|
||||
return createACLs('MyTestModel', [
|
||||
{permission: ACL.ALLOW, principalId: '$everyone'},
|
||||
{permission: ACL.DENY, principalId: '$authenticated'},
|
||||
{permission: ACL.DENY, principalId: '$authenticated'},
|
||||
{permission: ACL.ALLOW, principalId: 'myRole'},
|
||||
])
|
||||
.then(makeAuthorizedHttpRequestOnMyTestModel)
|
||||
.then(function() {
|
||||
var ctx = models.MyTestModel.lastRemotingContext;
|
||||
expect(ctx.args.options.authorizedRoles).to.eql(
|
||||
{
|
||||
$everyone: true,
|
||||
myRole: true,
|
||||
}
|
||||
);
|
||||
});
|
||||
.then(makeAuthorizedHttpRequestOnMyTestModel)
|
||||
.then(function() {
|
||||
const ctx = models.MyTestModel.lastRemotingContext;
|
||||
expect(ctx.args.options.authorizedRoles).to.eql(
|
||||
{
|
||||
$everyone: true,
|
||||
myRole: true,
|
||||
},
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
it('honors default permission setting', function() {
|
||||
|
@ -545,17 +602,17 @@ describe('authorized roles propagation in RemotingContext', function() {
|
|||
|
||||
return createACLs('MyTestModel', [
|
||||
{permission: ACL.DEFAULT, principalId: '$everyone'},
|
||||
{permission: ACL.DENY, principalId: '$authenticated'},
|
||||
{permission: ACL.ALLOW, principalId: 'myRole'},
|
||||
{permission: ACL.DENY, principalId: '$authenticated'},
|
||||
{permission: ACL.ALLOW, principalId: 'myRole'},
|
||||
])
|
||||
.then(makeAuthorizedHttpRequestOnMyTestModel)
|
||||
.then(function() {
|
||||
var ctx = models.MyTestModel.lastRemotingContext;
|
||||
expect(ctx.args.options.authorizedRoles).to.eql(
|
||||
.then(makeAuthorizedHttpRequestOnMyTestModel)
|
||||
.then(function() {
|
||||
const ctx = models.MyTestModel.lastRemotingContext;
|
||||
expect(ctx.args.options.authorizedRoles).to.eql(
|
||||
// '$everyone' is not expected as default permission is DENY
|
||||
{myRole: true}
|
||||
);
|
||||
});
|
||||
{myRole: true},
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
// helpers
|
||||
|
@ -569,6 +626,9 @@ describe('authorized roles propagation in RemotingContext', function() {
|
|||
app.enableAuth({dataSource: 'db'});
|
||||
models = app.models;
|
||||
|
||||
// Speed up the password hashing algorithm for tests
|
||||
models.User.settings.saltWorkFactor = 4;
|
||||
|
||||
// creating a custom model
|
||||
const MyTestModel = app.registry.createModel('MyTestModel');
|
||||
app.model(MyTestModel, {dataSource: 'db'});
|
||||
|
@ -584,15 +644,15 @@ describe('authorized roles propagation in RemotingContext', function() {
|
|||
models.User.create({username: 'myUser', email: 'myuser@example.com', password: 'pass'}),
|
||||
models.Role.create({name: 'myRole'}),
|
||||
])
|
||||
.spread(function(myUser, myRole) {
|
||||
return Promise.all([
|
||||
myRole.principals.create({principalType: 'USER', principalId: myUser.id}),
|
||||
models.User.login({username: 'myUser', password: 'pass'}),
|
||||
]);
|
||||
})
|
||||
.spread(function(role, token) {
|
||||
accessToken = token;
|
||||
});
|
||||
.spread(function(myUser, myRole) {
|
||||
return Promise.all([
|
||||
myRole.principals.create({principalType: 'USER', principalId: myUser.id}),
|
||||
models.User.login({username: 'myUser', password: 'pass'}),
|
||||
]);
|
||||
})
|
||||
.spread(function(role, token) {
|
||||
accessToken = token;
|
||||
});
|
||||
}
|
||||
|
||||
function createACLs(model, acls) {
|
||||
|
@ -607,7 +667,7 @@ describe('authorized roles propagation in RemotingContext', function() {
|
|||
});
|
||||
});
|
||||
return Promise.all(acls);
|
||||
};
|
||||
}
|
||||
|
||||
function makeAuthorizedHttpRequestOnMyTestModel() {
|
||||
return request.get('/MyTestModels')
|
||||
|
@ -615,3 +675,14 @@ describe('authorized roles propagation in RemotingContext', function() {
|
|||
.expect(200);
|
||||
}
|
||||
});
|
||||
|
||||
function setupTestModels() {
|
||||
ds = this.ds = loopback.createDataSource({connector: loopback.Memory});
|
||||
testModel = loopback.PersistedModel.extend('testModel');
|
||||
ACL.attachTo(ds);
|
||||
Role.attachTo(ds);
|
||||
RoleMapping.attachTo(ds);
|
||||
User.attachTo(ds);
|
||||
Scope.attachTo(ds);
|
||||
testModel.attachTo(ds);
|
||||
}
|
||||
|
|
321
test/app.test.js
321
test/app.test.js
|
@ -1,38 +1,39 @@
|
|||
// Copyright IBM Corp. 2013,2016. All Rights Reserved.
|
||||
// Copyright IBM Corp. 2013,2019. All Rights Reserved.
|
||||
// Node module: loopback
|
||||
// This file is licensed under the MIT License.
|
||||
// License text available at https://opensource.org/licenses/MIT
|
||||
|
||||
'use strict';
|
||||
var assert = require('assert');
|
||||
var async = require('async');
|
||||
var path = require('path');
|
||||
const assert = require('assert');
|
||||
const async = require('async');
|
||||
const path = require('path');
|
||||
|
||||
var http = require('http');
|
||||
var express = require('express');
|
||||
var loopback = require('../');
|
||||
var PersistedModel = loopback.PersistedModel;
|
||||
const http = require('http');
|
||||
const express = require('express');
|
||||
const loopback = require('../');
|
||||
const PersistedModel = loopback.PersistedModel;
|
||||
|
||||
var describe = require('./util/describe');
|
||||
var expect = require('./helpers/expect');
|
||||
var it = require('./util/it');
|
||||
var request = require('supertest');
|
||||
const describe = require('./util/describe');
|
||||
const expect = require('./helpers/expect');
|
||||
const it = require('./util/it');
|
||||
const request = require('supertest');
|
||||
const sinon = require('sinon');
|
||||
|
||||
describe('app', function() {
|
||||
var app;
|
||||
let app;
|
||||
beforeEach(function() {
|
||||
app = loopback();
|
||||
app = loopback({localRegistry: true, loadBuiltinModels: true});
|
||||
});
|
||||
|
||||
describe.onServer('.middleware(phase, handler)', function() {
|
||||
var steps;
|
||||
let steps;
|
||||
|
||||
beforeEach(function setup() {
|
||||
steps = [];
|
||||
});
|
||||
|
||||
it('runs middleware in phases', function(done) {
|
||||
var PHASES = [
|
||||
const PHASES = [
|
||||
'initial', 'session', 'auth', 'parse',
|
||||
'routes', 'files', 'final',
|
||||
];
|
||||
|
@ -87,10 +88,10 @@ describe('app', function() {
|
|||
return namedHandler(name);
|
||||
}
|
||||
|
||||
var myHandler;
|
||||
let myHandler;
|
||||
app.middleware('routes:before',
|
||||
myHandler = handlerThatAddsHandler('my-handler'));
|
||||
var found = app._findLayerByHandler(myHandler);
|
||||
const found = app._findLayerByHandler(myHandler);
|
||||
expect(found).to.be.an('object');
|
||||
expect(myHandler).to.equal(found.handle);
|
||||
expect(found).have.property('phase', 'routes:before');
|
||||
|
@ -105,13 +106,35 @@ describe('app', function() {
|
|||
|
||||
it('allows handlers to be wrapped as __NR_handler on express stack',
|
||||
function(done) {
|
||||
var myHandler = namedHandler('my-handler');
|
||||
var wrappedHandler = function(req, res, next) {
|
||||
const myHandler = namedHandler('my-handler');
|
||||
const wrappedHandler = function(req, res, next) {
|
||||
myHandler(req, res, next);
|
||||
};
|
||||
wrappedHandler['__NR_handler'] = myHandler;
|
||||
app.middleware('routes:before', wrappedHandler);
|
||||
var found = app._findLayerByHandler(myHandler);
|
||||
const found = app._findLayerByHandler(myHandler);
|
||||
expect(found).to.be.an('object');
|
||||
expect(found).have.property('phase', 'routes:before');
|
||||
executeMiddlewareHandlers(app, function(err) {
|
||||
if (err) return done(err);
|
||||
|
||||
expect(steps).to.eql(['my-handler']);
|
||||
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('allows handlers to be wrapped as __appdynamicsProxyInfo__ on express stack',
|
||||
function(done) {
|
||||
const myHandler = namedHandler('my-handler');
|
||||
const wrappedHandler = function(req, res, next) {
|
||||
myHandler(req, res, next);
|
||||
};
|
||||
wrappedHandler['__appdynamicsProxyInfo__'] = {
|
||||
orig: myHandler,
|
||||
};
|
||||
app.middleware('routes:before', wrappedHandler);
|
||||
const found = app._findLayerByHandler(myHandler);
|
||||
expect(found).to.be.an('object');
|
||||
expect(found).have.property('phase', 'routes:before');
|
||||
executeMiddlewareHandlers(app, function(err) {
|
||||
|
@ -125,13 +148,13 @@ describe('app', function() {
|
|||
|
||||
it('allows handlers to be wrapped as a property on express stack',
|
||||
function(done) {
|
||||
var myHandler = namedHandler('my-handler');
|
||||
var wrappedHandler = function(req, res, next) {
|
||||
const myHandler = namedHandler('my-handler');
|
||||
const wrappedHandler = function(req, res, next) {
|
||||
myHandler(req, res, next);
|
||||
};
|
||||
wrappedHandler['__handler'] = myHandler;
|
||||
app.middleware('routes:before', wrappedHandler);
|
||||
var found = app._findLayerByHandler(myHandler);
|
||||
const found = app._findLayerByHandler(myHandler);
|
||||
expect(found).to.be.an('object');
|
||||
expect(found).have.property('phase', 'routes:before');
|
||||
executeMiddlewareHandlers(app, function(err) {
|
||||
|
@ -144,7 +167,7 @@ describe('app', function() {
|
|||
});
|
||||
|
||||
it('injects error from previous phases into the router', function(done) {
|
||||
var expectedError = new Error('expected error');
|
||||
const expectedError = new Error('expected error');
|
||||
|
||||
app.middleware('initial', function(req, res, next) {
|
||||
steps.push('initial');
|
||||
|
@ -170,7 +193,7 @@ describe('app', function() {
|
|||
});
|
||||
|
||||
it('passes unhandled error to callback', function(done) {
|
||||
var expectedError = new Error('expected error');
|
||||
const expectedError = new Error('expected error');
|
||||
|
||||
app.middleware('initial', function(req, res, next) {
|
||||
next(expectedError);
|
||||
|
@ -184,8 +207,8 @@ describe('app', function() {
|
|||
});
|
||||
|
||||
it('passes errors to error handlers in the same phase', function(done) {
|
||||
var expectedError = new Error('this should be handled by middleware');
|
||||
var handledError;
|
||||
const expectedError = new Error('this should be handled by middleware');
|
||||
let handledError;
|
||||
|
||||
app.middleware('initial', function(req, res, next) {
|
||||
// continue in the next tick, this verifies that the next
|
||||
|
@ -222,7 +245,8 @@ describe('app', function() {
|
|||
expect(steps).to.eql(['/scope', '/scope/item']);
|
||||
|
||||
done();
|
||||
});
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
it('scopes middleware to a regex path', function(done) {
|
||||
|
@ -237,7 +261,8 @@ describe('app', function() {
|
|||
expect(steps).to.eql(['/a', '/b']);
|
||||
|
||||
done();
|
||||
});
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
it('scopes middleware to a list of scopes', function(done) {
|
||||
|
@ -252,7 +277,8 @@ describe('app', function() {
|
|||
expect(steps).to.eql(['/a', '/b', '/scope']);
|
||||
|
||||
done();
|
||||
});
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
it('sets req.url to a sub-path', function(done) {
|
||||
|
@ -272,7 +298,7 @@ describe('app', function() {
|
|||
});
|
||||
|
||||
it('exposes express helpers on req and res objects', function(done) {
|
||||
var req, res;
|
||||
let req, res;
|
||||
|
||||
app.middleware('initial', function(rq, rs, next) {
|
||||
req = rq;
|
||||
|
@ -310,7 +336,7 @@ describe('app', function() {
|
|||
});
|
||||
|
||||
it('sets req.baseUrl and req.originalUrl', function(done) {
|
||||
var reqProps;
|
||||
let reqProps;
|
||||
app.middleware('initial', function(req, res, next) {
|
||||
reqProps = {baseUrl: req.baseUrl, originalUrl: req.originalUrl};
|
||||
|
||||
|
@ -348,7 +374,7 @@ describe('app', function() {
|
|||
|
||||
// we need at least 9 elements to expose non-stability
|
||||
// of the built-in sort function
|
||||
var numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9];
|
||||
const numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9];
|
||||
numbers.forEach(function(n) {
|
||||
app.middleware('routes', namedHandler(n));
|
||||
});
|
||||
|
@ -363,8 +389,8 @@ describe('app', function() {
|
|||
});
|
||||
|
||||
it('correctly mounts express apps', function(done) {
|
||||
var data, mountWasEmitted;
|
||||
var subapp = express();
|
||||
let data, mountWasEmitted;
|
||||
const subapp = express();
|
||||
subapp.use(function(req, res, next) {
|
||||
data = {
|
||||
mountpath: req.app.mountpath,
|
||||
|
@ -391,10 +417,10 @@ describe('app', function() {
|
|||
});
|
||||
|
||||
it('restores req & res on return from mounted express app', function(done) {
|
||||
var expected = {};
|
||||
var actual = {};
|
||||
const expected = {};
|
||||
const actual = {};
|
||||
|
||||
var subapp = express();
|
||||
const subapp = express();
|
||||
subapp.use(function verifyTestAssumptions(req, res, next) {
|
||||
expect(req.__proto__).to.not.equal(expected.req);
|
||||
expect(res.__proto__).to.not.equal(expected.res);
|
||||
|
@ -442,8 +468,8 @@ describe('app', function() {
|
|||
}
|
||||
|
||||
function getObjectAndPrototypeKeys(obj) {
|
||||
var result = [];
|
||||
for (var k in obj) {
|
||||
const result = [];
|
||||
for (const k in obj) {
|
||||
result.push(k);
|
||||
}
|
||||
result.sort();
|
||||
|
@ -453,11 +479,11 @@ describe('app', function() {
|
|||
|
||||
describe.onServer('.middlewareFromConfig', function() {
|
||||
it('provides API for loading middleware from JSON config', function(done) {
|
||||
var steps = [];
|
||||
var expectedConfig = {key: 'value'};
|
||||
const steps = [];
|
||||
const expectedConfig = {key: 'value'};
|
||||
|
||||
var handlerFactory = function() {
|
||||
var args = Array.prototype.slice.apply(arguments);
|
||||
const handlerFactory = function() {
|
||||
const args = Array.prototype.slice.apply(arguments);
|
||||
return function(req, res, next) {
|
||||
steps.push(args);
|
||||
|
||||
|
@ -523,8 +549,8 @@ describe('app', function() {
|
|||
});
|
||||
});
|
||||
|
||||
it('scopes middleware to a list of scopes', function(done) {
|
||||
var steps = [];
|
||||
it('scopes middleware from config to a list of scopes', function(done) {
|
||||
const steps = [];
|
||||
app.middlewareFromConfig(
|
||||
function factory() {
|
||||
return function(req, res, next) {
|
||||
|
@ -536,7 +562,8 @@ describe('app', function() {
|
|||
{
|
||||
phase: 'initial',
|
||||
paths: ['/scope', /^\/(a|b)/],
|
||||
});
|
||||
},
|
||||
);
|
||||
|
||||
async.eachSeries(
|
||||
['/', '/a', '/b', '/c', '/scope', '/other'],
|
||||
|
@ -547,12 +574,13 @@ describe('app', function() {
|
|||
expect(steps).to.eql(['/a', '/b', '/scope']);
|
||||
|
||||
done();
|
||||
});
|
||||
},
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe.onServer('.defineMiddlewarePhases(nameOrArray)', function() {
|
||||
var app;
|
||||
let app;
|
||||
beforeEach(function() {
|
||||
app = loopback();
|
||||
});
|
||||
|
@ -598,7 +626,7 @@ describe('app', function() {
|
|||
});
|
||||
|
||||
function verifyMiddlewarePhases(names, done) {
|
||||
var steps = [];
|
||||
const steps = [];
|
||||
names.forEach(function(it) {
|
||||
app.middleware(it, function(req, res, next) {
|
||||
steps.push(it);
|
||||
|
@ -618,7 +646,7 @@ describe('app', function() {
|
|||
});
|
||||
|
||||
describe('app.model(Model)', function() {
|
||||
var app, db, MyTestModel;
|
||||
let app, db, MyTestModel;
|
||||
beforeEach(function() {
|
||||
app = loopback();
|
||||
app.set('remoting', {errorHandler: {debug: true, log: false}});
|
||||
|
@ -627,7 +655,7 @@ describe('app', function() {
|
|||
});
|
||||
|
||||
it('Expose a `Model` to remote clients', function() {
|
||||
var Color = PersistedModel.extend('color', {name: String});
|
||||
const Color = PersistedModel.extend('color', {name: String});
|
||||
app.model(Color);
|
||||
Color.attachTo(db);
|
||||
|
||||
|
@ -635,22 +663,22 @@ describe('app', function() {
|
|||
});
|
||||
|
||||
it('uses singular name as app.remoteObjects() key', function() {
|
||||
var Color = PersistedModel.extend('color', {name: String});
|
||||
const Color = PersistedModel.extend('color', {name: String});
|
||||
app.model(Color);
|
||||
Color.attachTo(db);
|
||||
expect(app.remoteObjects()).to.eql({color: Color});
|
||||
});
|
||||
|
||||
it('uses singular name as shared class name', function() {
|
||||
var Color = PersistedModel.extend('color', {name: String});
|
||||
const Color = PersistedModel.extend('color', {name: String});
|
||||
app.model(Color);
|
||||
Color.attachTo(db);
|
||||
var classes = app.remotes().classes().map(function(c) { return c.name; });
|
||||
const classes = app.remotes().classes().map(function(c) { return c.name; });
|
||||
expect(classes).to.contain('color');
|
||||
});
|
||||
|
||||
it('registers existing models to app.models', function() {
|
||||
var Color = db.createModel('color', {name: String});
|
||||
const Color = db.createModel('color', {name: String});
|
||||
app.model(Color);
|
||||
expect(Color.app).to.be.equal(app);
|
||||
expect(Color.shared).to.equal(true);
|
||||
|
@ -659,9 +687,9 @@ describe('app', function() {
|
|||
});
|
||||
|
||||
it('emits a `modelRemoted` event', function() {
|
||||
var Color = PersistedModel.extend('color', {name: String});
|
||||
const Color = PersistedModel.extend('color', {name: String});
|
||||
Color.shared = true;
|
||||
var remotedClass;
|
||||
let remotedClass;
|
||||
app.on('modelRemoted', function(sharedClass) {
|
||||
remotedClass = sharedClass;
|
||||
});
|
||||
|
@ -671,9 +699,9 @@ describe('app', function() {
|
|||
});
|
||||
|
||||
it('emits a `remoteMethodDisabled` event', function() {
|
||||
var Color = PersistedModel.extend('color', {name: String});
|
||||
const Color = PersistedModel.extend('color', {name: String});
|
||||
Color.shared = true;
|
||||
var remoteMethodDisabledClass, disabledRemoteMethod;
|
||||
let remoteMethodDisabledClass, disabledRemoteMethod;
|
||||
app.on('remoteMethodDisabled', function(sharedClass, methodName) {
|
||||
remoteMethodDisabledClass = sharedClass;
|
||||
disabledRemoteMethod = methodName;
|
||||
|
@ -688,23 +716,23 @@ describe('app', function() {
|
|||
|
||||
it('emits a `remoteMethodAdded` event', function() {
|
||||
app.dataSource('db', {connector: 'memory'});
|
||||
var Book = app.registry.createModel(
|
||||
const Book = app.registry.createModel(
|
||||
'Book',
|
||||
{name: 'string'},
|
||||
{plural: 'books'}
|
||||
{plural: 'books'},
|
||||
);
|
||||
app.model(Book, {dataSource: 'db'});
|
||||
|
||||
var Page = app.registry.createModel(
|
||||
const Page = app.registry.createModel(
|
||||
'Page',
|
||||
{name: 'string'},
|
||||
{plural: 'pages'}
|
||||
{plural: 'pages'},
|
||||
);
|
||||
app.model(Page, {dataSource: 'db'});
|
||||
|
||||
Book.hasMany(Page);
|
||||
|
||||
var remoteMethodAddedClass;
|
||||
let remoteMethodAddedClass;
|
||||
app.on('remoteMethodAdded', function(sharedClass) {
|
||||
remoteMethodAddedClass = sharedClass;
|
||||
});
|
||||
|
@ -713,17 +741,6 @@ describe('app', function() {
|
|||
expect(remoteMethodAddedClass).to.eql(Book.sharedClass);
|
||||
});
|
||||
|
||||
it.onServer('updates REST API when a new model is added', function(done) {
|
||||
app.use(loopback.rest());
|
||||
request(app).get('/colors').expect(404, function(err, res) {
|
||||
if (err) return done(err);
|
||||
var Color = PersistedModel.extend('color', {name: String});
|
||||
app.model(Color);
|
||||
Color.attachTo(db);
|
||||
request(app).get('/colors').expect(200, done);
|
||||
});
|
||||
});
|
||||
|
||||
it('accepts null dataSource', function(done) {
|
||||
app.model(MyTestModel, {dataSource: null});
|
||||
expect(MyTestModel.dataSource).to.eql(null);
|
||||
|
@ -742,14 +759,14 @@ describe('app', function() {
|
|||
});
|
||||
|
||||
it('throws error if model typeof string is passed', function() {
|
||||
var fn = function() { app.model('MyTestModel'); };
|
||||
const fn = function() { app.model('MyTestModel'); };
|
||||
expect(fn).to.throw(/app(\.model|\.registry)/);
|
||||
});
|
||||
});
|
||||
|
||||
describe('app.model(ModelCtor, config)', function() {
|
||||
it('attaches the model to a datasource', function() {
|
||||
var previousModel = loopback.registry.findModel('TestModel');
|
||||
const previousModel = loopback.registry.findModel('TestModel');
|
||||
app.dataSource('db', {connector: 'memory'});
|
||||
|
||||
if (previousModel) {
|
||||
|
@ -757,19 +774,79 @@ describe('app', function() {
|
|||
}
|
||||
|
||||
assert(!previousModel || !previousModel.dataSource);
|
||||
var TestModel = app.registry.createModel('TestModel');
|
||||
const TestModel = app.registry.createModel('TestModel');
|
||||
app.model(TestModel, {dataSource: 'db'});
|
||||
expect(app.models.TestModel.dataSource).to.equal(app.dataSources.db);
|
||||
});
|
||||
});
|
||||
|
||||
describe('app.deleteModelByName()', () => {
|
||||
let TestModel;
|
||||
beforeEach(setupTestModel);
|
||||
|
||||
it('removes the model from app registries', () => {
|
||||
expect(Object.keys(app.models))
|
||||
.to.contain('test-model')
|
||||
.and.contain('TestModel')
|
||||
.and.contain('testModel');
|
||||
expect(app.models().map(m => m.modelName))
|
||||
.to.contain('test-model');
|
||||
|
||||
app.deleteModelByName('test-model');
|
||||
|
||||
expect(Object.keys(app.models))
|
||||
.to.not.contain('test-model')
|
||||
.and.not.contain('TestModel')
|
||||
.and.not.contain('testModel');
|
||||
expect(app.models().map(m => m.modelName))
|
||||
.to.not.contain('test-model');
|
||||
});
|
||||
|
||||
it('removes the model from juggler registries', () => {
|
||||
expect(Object.keys(app.registry.modelBuilder.models))
|
||||
.to.contain('test-model');
|
||||
|
||||
app.deleteModelByName('test-model');
|
||||
|
||||
expect(Object.keys(app.registry.modelBuilder.models))
|
||||
.to.not.contain('test-model');
|
||||
});
|
||||
|
||||
it('removes the model from remoting registries', () => {
|
||||
expect(Object.keys(app.remotes()._classes))
|
||||
.to.contain('test-model');
|
||||
|
||||
app.deleteModelByName('test-model');
|
||||
|
||||
expect(Object.keys(app.remotes()._classes))
|
||||
.to.not.contain('test-model');
|
||||
});
|
||||
|
||||
it('emits "modelDeleted" event', () => {
|
||||
const spy = sinon.spy();
|
||||
app.on('modelDeleted', spy);
|
||||
|
||||
app.deleteModelByName('test-model');
|
||||
|
||||
sinon.assert.calledWith(spy, TestModel);
|
||||
});
|
||||
|
||||
function setupTestModel() {
|
||||
TestModel = app.registry.createModel({
|
||||
name: 'test-model',
|
||||
base: 'Model',
|
||||
});
|
||||
app.model(TestModel, {dataSource: null});
|
||||
}
|
||||
});
|
||||
|
||||
describe('app.models', function() {
|
||||
it('is unique per app instance', function() {
|
||||
app.dataSource('db', {connector: 'memory'});
|
||||
var Color = app.registry.createModel('Color');
|
||||
const Color = app.registry.createModel('Color');
|
||||
app.model(Color, {dataSource: 'db'});
|
||||
expect(app.models.Color).to.equal(Color);
|
||||
var anotherApp = loopback();
|
||||
const anotherApp = loopback();
|
||||
expect(anotherApp.models.Color).to.equal(undefined);
|
||||
});
|
||||
});
|
||||
|
@ -778,7 +855,7 @@ describe('app', function() {
|
|||
it('is unique per app instance', function() {
|
||||
app.dataSource('ds', {connector: 'memory'});
|
||||
expect(app.datasources.ds).to.not.equal(undefined);
|
||||
var anotherApp = loopback();
|
||||
const anotherApp = loopback();
|
||||
expect(anotherApp.datasources.ds).to.equal(undefined);
|
||||
});
|
||||
});
|
||||
|
@ -787,7 +864,7 @@ describe('app', function() {
|
|||
it('looks up the connector in `app.connectors`', function() {
|
||||
app.connector('custom', loopback.Memory);
|
||||
app.dataSource('custom', {connector: 'custom'});
|
||||
expect(app.dataSources.custom.name).to.equal(loopback.Memory.name);
|
||||
expect(app.dataSources.custom.name).to.equal('custom');
|
||||
});
|
||||
|
||||
it('adds data source name to error messages', function() {
|
||||
|
@ -809,11 +886,11 @@ describe('app', function() {
|
|||
|
||||
describe.onServer('listen()', function() {
|
||||
it('starts http server', function(done) {
|
||||
var app = loopback();
|
||||
const app = loopback();
|
||||
app.set('port', 0);
|
||||
app.get('/', function(req, res) { res.status(200).send('OK'); });
|
||||
|
||||
var server = app.listen();
|
||||
const server = app.listen();
|
||||
|
||||
expect(server).to.be.an.instanceOf(require('http').Server);
|
||||
|
||||
|
@ -823,7 +900,7 @@ describe('app', function() {
|
|||
});
|
||||
|
||||
it('updates port on `listening` event', function(done) {
|
||||
var app = loopback();
|
||||
const app = loopback();
|
||||
app.set('port', 0);
|
||||
|
||||
app.listen(function() {
|
||||
|
@ -834,12 +911,12 @@ describe('app', function() {
|
|||
});
|
||||
|
||||
it('updates `url` on `listening` event', function(done) {
|
||||
var app = loopback();
|
||||
const app = loopback();
|
||||
app.set('port', 0);
|
||||
app.set('host', undefined);
|
||||
|
||||
app.listen(function() {
|
||||
var expectedUrl = 'http://localhost:' + app.get('port') + '/';
|
||||
const expectedUrl = 'http://localhost:' + app.get('port') + '/';
|
||||
expect(app.get('url'), 'url').to.equal(expectedUrl);
|
||||
|
||||
done();
|
||||
|
@ -847,7 +924,7 @@ describe('app', function() {
|
|||
});
|
||||
|
||||
it('forwards to http.Server.listen on more than one arg', function(done) {
|
||||
var app = loopback();
|
||||
const app = loopback();
|
||||
app.set('port', 1);
|
||||
app.listen(0, '127.0.0.1', function() {
|
||||
expect(app.get('port'), 'port').to.not.equal(0).and.not.equal(1);
|
||||
|
@ -857,20 +934,18 @@ describe('app', function() {
|
|||
});
|
||||
});
|
||||
|
||||
it('forwards to http.Server.listen when the single arg is not a function',
|
||||
function(done) {
|
||||
var app = loopback();
|
||||
app.set('port', 1);
|
||||
app.listen(0).on('listening', function() {
|
||||
expect(app.get('port'), 'port') .to.not.equal(0).and.not.equal(1);
|
||||
it('forwards to http.Server.listen when the single arg is not a function', function(done) {
|
||||
const app = loopback();
|
||||
app.set('port', 1);
|
||||
app.listen(0).on('listening', function() {
|
||||
expect(app.get('port'), 'port') .to.not.equal(0).and.not.equal(1);
|
||||
|
||||
done();
|
||||
});
|
||||
}
|
||||
);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('uses app config when no parameter is supplied', function(done) {
|
||||
var app = loopback();
|
||||
const app = loopback();
|
||||
// Http listens on all interfaces by default
|
||||
// Custom host serves as an indicator whether
|
||||
// the value was used by app.listen
|
||||
|
@ -892,26 +967,26 @@ describe('app', function() {
|
|||
});
|
||||
|
||||
it('auto-configures required models to provided dataSource', function() {
|
||||
var AUTH_MODELS = ['User', 'ACL', 'AccessToken', 'Role', 'RoleMapping'];
|
||||
var app = loopback({localRegistry: true, loadBuiltinModels: true});
|
||||
const AUTH_MODELS = ['User', 'ACL', 'AccessToken', 'Role', 'RoleMapping'];
|
||||
const app = loopback({localRegistry: true, loadBuiltinModels: true});
|
||||
require('../lib/builtin-models')(app.registry);
|
||||
var db = app.dataSource('db', {connector: 'memory'});
|
||||
const db = app.dataSource('db', {connector: 'memory'});
|
||||
|
||||
app.enableAuth({dataSource: 'db'});
|
||||
|
||||
expect(Object.keys(app.models)).to.include.members(AUTH_MODELS);
|
||||
|
||||
AUTH_MODELS.forEach(function(m) {
|
||||
var Model = app.models[m];
|
||||
const Model = app.models[m];
|
||||
expect(Model.dataSource, m + '.dataSource').to.equal(db);
|
||||
expect(Model.shared, m + '.shared').to.equal(m === 'User');
|
||||
});
|
||||
});
|
||||
|
||||
it('detects already configured subclass of a required model', function() {
|
||||
var app = loopback({localRegistry: true, loadBuiltinModels: true});
|
||||
var db = app.dataSource('db', {connector: 'memory'});
|
||||
var Customer = app.registry.createModel('Customer', {}, {base: 'User'});
|
||||
const app = loopback({localRegistry: true, loadBuiltinModels: true});
|
||||
const db = app.dataSource('db', {connector: 'memory'});
|
||||
const Customer = app.registry.createModel('Customer', {}, {base: 'User'});
|
||||
app.model(Customer, {dataSource: 'db'});
|
||||
|
||||
// Fix AccessToken's "belongsTo user" relation to use our new Customer model
|
||||
|
@ -926,7 +1001,7 @@ describe('app', function() {
|
|||
|
||||
describe.onServer('app.get(\'/\', loopback.status())', function() {
|
||||
it('should return the status of the application', function(done) {
|
||||
var app = loopback();
|
||||
const app = loopback();
|
||||
app.get('/', loopback.status());
|
||||
request(app)
|
||||
.get('/')
|
||||
|
@ -938,7 +1013,7 @@ describe('app', function() {
|
|||
expect(res.body).to.have.property('started');
|
||||
expect(res.body.uptime, 'uptime').to.be.gte(0);
|
||||
|
||||
var elapsed = Date.now() - Number(new Date(res.body.started));
|
||||
const elapsed = Date.now() - Number(new Date(res.body.started));
|
||||
|
||||
// elapsed should be a small positive number...
|
||||
expect(elapsed, 'elapsed').to.be.within(0, 300);
|
||||
|
@ -951,7 +1026,7 @@ describe('app', function() {
|
|||
describe('app.connectors', function() {
|
||||
it('is unique per app instance', function() {
|
||||
app.connectors.foo = 'bar';
|
||||
var anotherApp = loopback();
|
||||
const anotherApp = loopback();
|
||||
expect(anotherApp.connectors.foo).to.equal(undefined);
|
||||
});
|
||||
|
||||
|
@ -994,8 +1069,8 @@ describe('app', function() {
|
|||
});
|
||||
|
||||
it('is unique per app instance', function() {
|
||||
var app1 = loopback();
|
||||
var app2 = loopback();
|
||||
const app1 = loopback();
|
||||
const app2 = loopback();
|
||||
|
||||
expect(app1.settings).to.not.equal(app2.settings);
|
||||
|
||||
|
@ -1005,20 +1080,20 @@ describe('app', function() {
|
|||
});
|
||||
|
||||
it('exposes loopback as a property', function() {
|
||||
var app = loopback();
|
||||
const app = loopback();
|
||||
expect(app.loopback).to.equal(loopback);
|
||||
});
|
||||
|
||||
function setupUserModels(app, options, done) {
|
||||
app.dataSource('db', {connector: 'memory'});
|
||||
var UserAccount = app.registry.createModel(
|
||||
const UserAccount = app.registry.createModel(
|
||||
'UserAccount',
|
||||
{name: 'string'},
|
||||
options
|
||||
options,
|
||||
);
|
||||
var UserRole = app.registry.createModel(
|
||||
const UserRole = app.registry.createModel(
|
||||
'UserRole',
|
||||
{name: 'string'}
|
||||
{name: 'string'},
|
||||
);
|
||||
app.model(UserAccount, {dataSource: 'db'});
|
||||
app.model(UserRole, {dataSource: 'db'});
|
||||
|
@ -1033,7 +1108,7 @@ describe('app', function() {
|
|||
}
|
||||
|
||||
describe('Model-level normalizeHttpPath option', function() {
|
||||
var app;
|
||||
let app;
|
||||
beforeEach(function() {
|
||||
app = loopback();
|
||||
});
|
||||
|
@ -1069,7 +1144,7 @@ describe('app', function() {
|
|||
});
|
||||
});
|
||||
describe('app-level normalizeHttpPath option', function() {
|
||||
var app;
|
||||
let app;
|
||||
beforeEach(function() {
|
||||
app = loopback();
|
||||
});
|
||||
|
@ -1104,7 +1179,7 @@ describe('app', function() {
|
|||
});
|
||||
|
||||
describe('Model-level and app-level normalizeHttpPath options', function() {
|
||||
var app;
|
||||
let app;
|
||||
beforeEach(function() {
|
||||
app = loopback();
|
||||
});
|
||||
|
@ -1128,8 +1203,8 @@ describe('app', function() {
|
|||
});
|
||||
|
||||
function executeMiddlewareHandlers(app, urlPath, callback) {
|
||||
var handlerError = undefined;
|
||||
var server = http.createServer(function(req, res) {
|
||||
let handlerError = undefined;
|
||||
const server = http.createServer(function(req, res) {
|
||||
app.handle(req, res, function(err) {
|
||||
if (err) {
|
||||
handlerError = err;
|
||||
|
|
|
@ -1,8 +1,17 @@
|
|||
// Copyright IBM Corp. 2017,2018. All Rights Reserved.
|
||||
// Node module: loopback
|
||||
// This file is licensed under the MIT License.
|
||||
// License text available at https://opensource.org/licenses/MIT
|
||||
|
||||
'use strict';
|
||||
|
||||
const loopback = require('../');
|
||||
const supertest = require('supertest');
|
||||
const strongErrorHandler = require('strong-error-handler');
|
||||
const loggers = require('./helpers/error-loggers');
|
||||
|
||||
const logAllServerErrors = loggers.logAllServerErrors;
|
||||
const logServerErrorsOtherThan = loggers.logServerErrorsOtherThan;
|
||||
|
||||
describe('Authorization scopes', () => {
|
||||
const CUSTOM_SCOPE = 'read:custom';
|
||||
|
@ -15,28 +24,28 @@ describe('Authorization scopes', () => {
|
|||
beforeEach(givenScopedToken);
|
||||
|
||||
it('denies regular token to invoke custom-scoped method', () => {
|
||||
logServerErrorsOtherThan(401);
|
||||
logServerErrorsOtherThan(401, app);
|
||||
return request.get('/users/scoped')
|
||||
.set('Authorization', regularToken.id)
|
||||
.expect(401);
|
||||
});
|
||||
|
||||
it('allows regular tokens to invoke default-scoped method', () => {
|
||||
logAllServerErrors();
|
||||
logAllServerErrors(app);
|
||||
return request.get('/users/' + testUser.id)
|
||||
.set('Authorization', regularToken.id)
|
||||
.expect(200);
|
||||
});
|
||||
|
||||
it('allows scoped token to invoke custom-scoped method', () => {
|
||||
logAllServerErrors();
|
||||
logAllServerErrors(app);
|
||||
return request.get('/users/scoped')
|
||||
.set('Authorization', scopedToken.id)
|
||||
.expect(204);
|
||||
});
|
||||
|
||||
it('denies scoped token to invoke default-scoped method', () => {
|
||||
logServerErrorsOtherThan(401);
|
||||
logServerErrorsOtherThan(401, app);
|
||||
return request.get('/users/' + testUser.id)
|
||||
.set('Authorization', scopedToken.id)
|
||||
.expect(401);
|
||||
|
@ -45,7 +54,7 @@ describe('Authorization scopes', () => {
|
|||
describe('token granted both default and custom scope', () => {
|
||||
beforeEach('given token with default and custom scope',
|
||||
() => givenScopedToken(['DEFAULT', CUSTOM_SCOPE]));
|
||||
beforeEach(logAllServerErrors);
|
||||
beforeEach(() => logAllServerErrors(app));
|
||||
|
||||
it('allows invocation of default-scoped method', () => {
|
||||
return request.get('/users/' + testUser.id)
|
||||
|
@ -116,19 +125,4 @@ describe('Authorization scopes', () => {
|
|||
return testUser.accessTokens.create({ttl: 60, scopes})
|
||||
.then(t => scopedToken = t);
|
||||
}
|
||||
|
||||
function logAllServerErrors() {
|
||||
logServerErrorsOtherThan(-1);
|
||||
}
|
||||
|
||||
function logServerErrorsOtherThan(statusCode) {
|
||||
app.use((err, req, res, next) => {
|
||||
if ((err.statusCode || 500) !== statusCode) {
|
||||
console.log('Unhandled error for request %s %s: %s',
|
||||
req.method, req.url, err.stack || err);
|
||||
}
|
||||
res.statusCode = err.statusCode || 500;
|
||||
res.json(err);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
|
|
@ -1,20 +1,20 @@
|
|||
// Copyright IBM Corp. 2015,2016. All Rights Reserved.
|
||||
// Copyright IBM Corp. 2015,2019. All Rights Reserved.
|
||||
// Node module: loopback
|
||||
// This file is licensed under the MIT License.
|
||||
// License text available at https://opensource.org/licenses/MIT
|
||||
|
||||
'use strict';
|
||||
var expect = require('./helpers/expect');
|
||||
var sinon = require('sinon');
|
||||
var loopback = require('../');
|
||||
const expect = require('./helpers/expect');
|
||||
const sinon = require('sinon');
|
||||
const loopback = require('../');
|
||||
|
||||
describe('PersistedModel.createChangeStream()', function() {
|
||||
describe('configured to source changes locally', function() {
|
||||
before(function() {
|
||||
var test = this;
|
||||
var app = loopback({localRegistry: true});
|
||||
var ds = app.dataSource('ds', {connector: 'memory'});
|
||||
var Score = app.registry.createModel('Score');
|
||||
const test = this;
|
||||
const app = loopback({localRegistry: true});
|
||||
const ds = app.dataSource('ds', {connector: 'memory'});
|
||||
const Score = app.registry.createModel('Score');
|
||||
this.Score = app.model(Score, {
|
||||
dataSource: 'ds',
|
||||
changeDataSource: false, // use only local observers
|
||||
|
@ -24,7 +24,7 @@ describe('PersistedModel.createChangeStream()', function() {
|
|||
afterEach(verifyObserversRemoval);
|
||||
|
||||
it('should detect create', function(done) {
|
||||
var Score = this.Score;
|
||||
const Score = this.Score;
|
||||
|
||||
Score.createChangeStream(function(err, changes) {
|
||||
changes.on('data', function(change) {
|
||||
|
@ -38,7 +38,7 @@ describe('PersistedModel.createChangeStream()', function() {
|
|||
});
|
||||
|
||||
it('should detect update', function(done) {
|
||||
var Score = this.Score;
|
||||
const Score = this.Score;
|
||||
Score.create({team: 'foo'}, function(err, newScore) {
|
||||
Score.createChangeStream(function(err, changes) {
|
||||
changes.on('data', function(change) {
|
||||
|
@ -55,7 +55,7 @@ describe('PersistedModel.createChangeStream()', function() {
|
|||
});
|
||||
|
||||
it('should detect delete', function(done) {
|
||||
var Score = this.Score;
|
||||
const Score = this.Score;
|
||||
Score.create({team: 'foo'}, function(err, newScore) {
|
||||
Score.createChangeStream(function(err, changes) {
|
||||
changes.on('data', function(change) {
|
||||
|
@ -70,10 +70,42 @@ describe('PersistedModel.createChangeStream()', function() {
|
|||
});
|
||||
});
|
||||
|
||||
it('should not emit changes after destroy', function(done) {
|
||||
var Score = this.Score;
|
||||
it('should apply "where" and "fields" to create events', function() {
|
||||
const Score = this.Score;
|
||||
const data = [
|
||||
{team: 'baz', player: 'baz', value: 1},
|
||||
{team: 'bar', player: 'baz', value: 2},
|
||||
{team: 'foo', player: 'bar', value: 3},
|
||||
];
|
||||
const options = {where: {player: 'bar'}, fields: ['team', 'value']};
|
||||
const changes = [];
|
||||
let changeStream;
|
||||
|
||||
var spy = sinon.spy();
|
||||
return Score.createChangeStream(options)
|
||||
.then(stream => {
|
||||
changeStream = stream;
|
||||
changeStream.on('data', function(change) {
|
||||
changes.push(change);
|
||||
});
|
||||
|
||||
return Score.create(data);
|
||||
})
|
||||
.then(scores => {
|
||||
changeStream.destroy();
|
||||
|
||||
expect(changes).to.have.length(1);
|
||||
expect(changes[0]).to.have.property('type', 'create');
|
||||
expect(changes[0].data).to.eql({
|
||||
'team': 'foo',
|
||||
value: 3,
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('should not emit changes after destroy', function(done) {
|
||||
const Score = this.Score;
|
||||
|
||||
const spy = sinon.spy();
|
||||
|
||||
Score.createChangeStream(function(err, changes) {
|
||||
changes.on('data', function() {
|
||||
|
@ -91,7 +123,7 @@ describe('PersistedModel.createChangeStream()', function() {
|
|||
});
|
||||
|
||||
function verifyObserversRemoval() {
|
||||
var Score = this.Score;
|
||||
const Score = this.Score;
|
||||
expect(Score._observers['after save']).to.be.empty();
|
||||
expect(Score._observers['after delete']).to.be.empty();
|
||||
}
|
||||
|
@ -100,10 +132,10 @@ describe('PersistedModel.createChangeStream()', function() {
|
|||
// TODO(ritch) implement multi-server support
|
||||
describe.skip('configured to source changes using pubsub', function() {
|
||||
before(function() {
|
||||
var test = this;
|
||||
var app = loopback({localRegistry: true});
|
||||
var db = app.dataSource('ds', {connector: 'memory'});
|
||||
var ps = app.dataSource('ps', {
|
||||
const test = this;
|
||||
const app = loopback({localRegistry: true});
|
||||
const db = app.dataSource('ds', {connector: 'memory'});
|
||||
const ps = app.dataSource('ps', {
|
||||
host: 'localhost',
|
||||
port: '12345',
|
||||
connector: 'pubsub',
|
||||
|
@ -116,7 +148,7 @@ describe('PersistedModel.createChangeStream()', function() {
|
|||
});
|
||||
|
||||
it('should detect a change', function(done) {
|
||||
var Score = this.Score;
|
||||
const Score = this.Score;
|
||||
|
||||
Score.createChangeStream(function(err, changes) {
|
||||
changes.on('data', function(change) {
|
||||
|
|
|
@ -1,19 +1,19 @@
|
|||
// Copyright IBM Corp. 2014,2016. All Rights Reserved.
|
||||
// Copyright IBM Corp. 2014,2019. All Rights Reserved.
|
||||
// Node module: loopback
|
||||
// This file is licensed under the MIT License.
|
||||
// License text available at https://opensource.org/licenses/MIT
|
||||
|
||||
'use strict';
|
||||
var assert = require('assert');
|
||||
var async = require('async');
|
||||
var expect = require('./helpers/expect');
|
||||
var loopback = require('../');
|
||||
const assert = require('assert');
|
||||
const async = require('async');
|
||||
const expect = require('./helpers/expect');
|
||||
const loopback = require('../');
|
||||
|
||||
describe('Change', function() {
|
||||
let Change, TestModel;
|
||||
|
||||
beforeEach(function() {
|
||||
var memory = loopback.createDataSource({
|
||||
const memory = loopback.createDataSource({
|
||||
connector: loopback.Memory,
|
||||
});
|
||||
TestModel = loopback.PersistedModel.extend('ChangeTestModel',
|
||||
|
@ -29,7 +29,7 @@ describe('Change', function() {
|
|||
});
|
||||
|
||||
beforeEach(function(done) {
|
||||
var test = this;
|
||||
const test = this;
|
||||
test.data = {
|
||||
foo: 'bar',
|
||||
};
|
||||
|
@ -52,13 +52,13 @@ describe('Change', function() {
|
|||
|
||||
describe('change.id', function() {
|
||||
it('should be a hash of the modelName and modelId', function() {
|
||||
var change = new Change({
|
||||
const change = new Change({
|
||||
rev: 'abc',
|
||||
modelName: 'foo',
|
||||
modelId: 'bar',
|
||||
});
|
||||
|
||||
var hash = Change.hash([change.modelName, change.modelId].join('-'));
|
||||
const hash = Change.hash([change.modelName, change.modelId].join('-'));
|
||||
|
||||
assert.equal(change.id, hash);
|
||||
});
|
||||
|
@ -67,7 +67,7 @@ describe('Change', function() {
|
|||
describe('Change.rectifyModelChanges(modelName, modelIds, callback)', function() {
|
||||
describe('using an existing untracked model', function() {
|
||||
beforeEach(function(done) {
|
||||
var test = this;
|
||||
const test = this;
|
||||
Change.rectifyModelChanges(this.modelName, [this.modelId], function(err, trackedChanges) {
|
||||
if (err) return done(err);
|
||||
|
||||
|
@ -76,7 +76,7 @@ describe('Change', function() {
|
|||
});
|
||||
|
||||
it('should create an entry', function(done) {
|
||||
var test = this;
|
||||
const test = this;
|
||||
Change.find(function(err, trackedChanges) {
|
||||
assert.equal(trackedChanges[0].modelId, test.modelId.toString());
|
||||
|
||||
|
@ -97,7 +97,7 @@ describe('Change', function() {
|
|||
describe('Change.rectifyModelChanges - promise variant', function() {
|
||||
describe('using an existing untracked model', function() {
|
||||
beforeEach(function(done) {
|
||||
var test = this;
|
||||
const test = this;
|
||||
Change.rectifyModelChanges(this.modelName, [this.modelId])
|
||||
.then(function(trackedChanges) {
|
||||
done();
|
||||
|
@ -106,7 +106,7 @@ describe('Change', function() {
|
|||
});
|
||||
|
||||
it('should create an entry', function(done) {
|
||||
var test = this;
|
||||
const test = this;
|
||||
Change.find()
|
||||
.then(function(trackedChanges) {
|
||||
assert.equal(trackedChanges[0].modelId, test.modelId.toString());
|
||||
|
@ -131,7 +131,7 @@ describe('Change', function() {
|
|||
describe('Change.findOrCreateChange(modelName, modelId, callback)', function() {
|
||||
describe('when a change doesnt exist', function() {
|
||||
beforeEach(function(done) {
|
||||
var test = this;
|
||||
const test = this;
|
||||
Change.findOrCreateChange(this.modelName, this.modelId, function(err, result) {
|
||||
if (err) return done(err);
|
||||
|
||||
|
@ -142,7 +142,7 @@ describe('Change', function() {
|
|||
});
|
||||
|
||||
it('should create an entry', function(done) {
|
||||
var test = this;
|
||||
const test = this;
|
||||
Change.findById(this.result.id, function(err, change) {
|
||||
if (err) return done(err);
|
||||
|
||||
|
@ -155,18 +155,18 @@ describe('Change', function() {
|
|||
|
||||
describe('when a change doesnt exist - promise variant', function() {
|
||||
beforeEach(function(done) {
|
||||
var test = this;
|
||||
const test = this;
|
||||
Change.findOrCreateChange(this.modelName, this.modelId)
|
||||
.then(function(result) {
|
||||
test.result = result;
|
||||
.then(function(result) {
|
||||
test.result = result;
|
||||
|
||||
done();
|
||||
})
|
||||
.catch(done);
|
||||
done();
|
||||
})
|
||||
.catch(done);
|
||||
});
|
||||
|
||||
it('should create an entry', function(done) {
|
||||
var test = this;
|
||||
const test = this;
|
||||
Change.findById(this.result.id, function(err, change) {
|
||||
if (err) return done(err);
|
||||
|
||||
|
@ -179,7 +179,7 @@ describe('Change', function() {
|
|||
|
||||
describe('when a change does exist', function() {
|
||||
beforeEach(function(done) {
|
||||
var test = this;
|
||||
const test = this;
|
||||
Change.create({
|
||||
modelName: test.modelName,
|
||||
modelId: test.modelId,
|
||||
|
@ -191,7 +191,7 @@ describe('Change', function() {
|
|||
});
|
||||
|
||||
beforeEach(function(done) {
|
||||
var test = this;
|
||||
const test = this;
|
||||
Change.findOrCreateChange(this.modelName, this.modelId, function(err, result) {
|
||||
if (err) return done(err);
|
||||
|
||||
|
@ -202,7 +202,7 @@ describe('Change', function() {
|
|||
});
|
||||
|
||||
it('should find the entry', function(done) {
|
||||
var test = this;
|
||||
const test = this;
|
||||
assert.equal(test.existingChange.id, test.result.id);
|
||||
|
||||
done();
|
||||
|
@ -211,7 +211,7 @@ describe('Change', function() {
|
|||
});
|
||||
|
||||
describe('change.rectify(callback)', function() {
|
||||
var change;
|
||||
let change;
|
||||
beforeEach(function(done) {
|
||||
Change.findOrCreate(
|
||||
{
|
||||
|
@ -222,11 +222,12 @@ describe('Change', function() {
|
|||
change = ch;
|
||||
|
||||
done(err);
|
||||
});
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
it('should create a new change with the correct revision', function(done) {
|
||||
var test = this;
|
||||
const test = this;
|
||||
change.rectify(function(err, ch) {
|
||||
assert.equal(ch.rev, test.revisionForModel);
|
||||
|
||||
|
@ -237,9 +238,9 @@ describe('Change', function() {
|
|||
// This test is a low-level equivalent of the test in replication.test.js
|
||||
// called "replicates multiple updates within the same CP"
|
||||
it('should merge updates within the same checkpoint', function(done) {
|
||||
var test = this;
|
||||
var originalRev = this.revisionForModel;
|
||||
var cp;
|
||||
const test = this;
|
||||
const originalRev = this.revisionForModel;
|
||||
let cp;
|
||||
|
||||
async.series([
|
||||
rectify,
|
||||
|
@ -273,7 +274,7 @@ describe('Change', function() {
|
|||
}
|
||||
|
||||
function update(next) {
|
||||
var model = test.model;
|
||||
const model = test.model;
|
||||
|
||||
model.name += 'updated';
|
||||
model.save(function(err) {
|
||||
|
@ -285,9 +286,9 @@ describe('Change', function() {
|
|||
});
|
||||
|
||||
it('should not change checkpoint when rev is the same', function(done) {
|
||||
var test = this;
|
||||
var originalCheckpoint = change.checkpoint;
|
||||
var originalRev = change.rev;
|
||||
const test = this;
|
||||
const originalCheckpoint = change.checkpoint;
|
||||
const originalRev = change.rev;
|
||||
|
||||
TestModel.checkpoint(function(err, inst) {
|
||||
if (err) return done(err);
|
||||
|
@ -305,7 +306,7 @@ describe('Change', function() {
|
|||
});
|
||||
|
||||
describe('change.rectify - promise variant', function() {
|
||||
var change;
|
||||
let change;
|
||||
beforeEach(function(done) {
|
||||
Change.findOrCreateChange(this.modelName, this.modelId)
|
||||
.then(function(ch) {
|
||||
|
@ -317,7 +318,7 @@ describe('Change', function() {
|
|||
});
|
||||
|
||||
it('should create a new change with the correct revision', function(done) {
|
||||
var test = this;
|
||||
const test = this;
|
||||
change.rectify()
|
||||
.then(function(ch) {
|
||||
assert.equal(ch.rev, test.revisionForModel);
|
||||
|
@ -329,8 +330,8 @@ describe('Change', function() {
|
|||
|
||||
describe('change.currentRevision(callback)', function() {
|
||||
it('should get the correct revision', function(done) {
|
||||
var test = this;
|
||||
var change = new Change({
|
||||
const test = this;
|
||||
const change = new Change({
|
||||
modelName: this.modelName,
|
||||
modelId: this.modelId,
|
||||
});
|
||||
|
@ -345,27 +346,27 @@ describe('Change', function() {
|
|||
|
||||
describe('change.currentRevision - promise variant', function() {
|
||||
it('should get the correct revision', function(done) {
|
||||
var test = this;
|
||||
var change = new Change({
|
||||
const test = this;
|
||||
const change = new Change({
|
||||
modelName: this.modelName,
|
||||
modelId: this.modelId,
|
||||
});
|
||||
|
||||
change.currentRevision()
|
||||
.then(function(rev) {
|
||||
assert.equal(rev, test.revisionForModel);
|
||||
.then(function(rev) {
|
||||
assert.equal(rev, test.revisionForModel);
|
||||
|
||||
done();
|
||||
})
|
||||
.catch(done);
|
||||
done();
|
||||
})
|
||||
.catch(done);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Change.hash(str)', function() {
|
||||
// todo(ritch) test other hashing algorithms
|
||||
it('should hash the given string', function() {
|
||||
var str = 'foo';
|
||||
var hash = Change.hash(str);
|
||||
const str = 'foo';
|
||||
const hash = Change.hash(str);
|
||||
assert(hash !== str);
|
||||
assert(typeof hash === 'string');
|
||||
});
|
||||
|
@ -373,54 +374,54 @@ describe('Change', function() {
|
|||
|
||||
describe('Change.revisionForInst(inst)', function() {
|
||||
it('should return the same revision for the same data', function() {
|
||||
var a = {
|
||||
const a = {
|
||||
b: {
|
||||
b: ['c', 'd'],
|
||||
c: ['d', 'e'],
|
||||
},
|
||||
};
|
||||
var b = {
|
||||
const b = {
|
||||
b: {
|
||||
c: ['d', 'e'],
|
||||
b: ['c', 'd'],
|
||||
},
|
||||
};
|
||||
|
||||
var aRev = Change.revisionForInst(a);
|
||||
var bRev = Change.revisionForInst(b);
|
||||
const aRev = Change.revisionForInst(a);
|
||||
const bRev = Change.revisionForInst(b);
|
||||
assert.equal(aRev, bRev);
|
||||
});
|
||||
});
|
||||
|
||||
describe('change.type()', function() {
|
||||
it('CREATE', function() {
|
||||
var change = new Change({
|
||||
const change = new Change({
|
||||
rev: this.revisionForModel,
|
||||
});
|
||||
assert.equal(Change.CREATE, change.type());
|
||||
});
|
||||
it('UPDATE', function() {
|
||||
var change = new Change({
|
||||
const change = new Change({
|
||||
rev: this.revisionForModel,
|
||||
prev: this.revisionForModel,
|
||||
});
|
||||
assert.equal(Change.UPDATE, change.type());
|
||||
});
|
||||
it('DELETE', function() {
|
||||
var change = new Change({
|
||||
const change = new Change({
|
||||
prev: this.revisionForModel,
|
||||
});
|
||||
assert.equal(Change.DELETE, change.type());
|
||||
});
|
||||
it('UNKNOWN', function() {
|
||||
var change = new Change();
|
||||
const change = new Change();
|
||||
assert.equal(Change.UNKNOWN, change.type());
|
||||
});
|
||||
});
|
||||
|
||||
describe('change.getModelCtor()', function() {
|
||||
it('should get the correct model class', function() {
|
||||
var change = new Change({
|
||||
const change = new Change({
|
||||
modelName: this.modelName,
|
||||
});
|
||||
|
||||
|
@ -430,11 +431,11 @@ describe('Change', function() {
|
|||
|
||||
describe('change.equals(otherChange)', function() {
|
||||
it('should return true when the change is equal', function() {
|
||||
var change = new Change({
|
||||
const change = new Change({
|
||||
rev: this.revisionForModel,
|
||||
});
|
||||
|
||||
var otherChange = new Change({
|
||||
const otherChange = new Change({
|
||||
rev: this.revisionForModel,
|
||||
});
|
||||
|
||||
|
@ -442,13 +443,13 @@ describe('Change', function() {
|
|||
});
|
||||
|
||||
it('should return true when both changes are deletes', function() {
|
||||
var REV = 'foo';
|
||||
var change = new Change({
|
||||
const REV = 'foo';
|
||||
const change = new Change({
|
||||
rev: null,
|
||||
prev: REV,
|
||||
});
|
||||
|
||||
var otherChange = new Change({
|
||||
const otherChange = new Change({
|
||||
rev: undefined,
|
||||
prev: REV,
|
||||
});
|
||||
|
@ -462,11 +463,11 @@ describe('Change', function() {
|
|||
|
||||
describe('change.isBasedOn(otherChange)', function() {
|
||||
it('should return true when the change is based on the other', function() {
|
||||
var change = new Change({
|
||||
const change = new Change({
|
||||
prev: this.revisionForModel,
|
||||
});
|
||||
|
||||
var otherChange = new Change({
|
||||
const otherChange = new Change({
|
||||
rev: this.revisionForModel,
|
||||
});
|
||||
|
||||
|
@ -484,7 +485,7 @@ describe('Change', function() {
|
|||
});
|
||||
|
||||
it('should return delta and conflict lists', function(done) {
|
||||
var remoteChanges = [
|
||||
const remoteChanges = [
|
||||
// an update => should result in a delta
|
||||
{rev: 'foo2', prev: 'foo', modelName: this.modelName, modelId: 9, checkpoint: 1},
|
||||
// no change => should not result in a delta / conflict
|
||||
|
@ -504,7 +505,7 @@ describe('Change', function() {
|
|||
});
|
||||
|
||||
it('should return delta and conflict lists - promise variant', function(done) {
|
||||
var remoteChanges = [
|
||||
const remoteChanges = [
|
||||
// an update => should result in a delta
|
||||
{rev: 'foo2', prev: 'foo', modelName: this.modelName, modelId: 9, checkpoint: 1},
|
||||
// no change => should not result in a delta / conflict
|
||||
|
@ -524,7 +525,7 @@ describe('Change', function() {
|
|||
});
|
||||
|
||||
it('should set "prev" to local revision in non-conflicting delta', function(done) {
|
||||
var updateRecord = {
|
||||
const updateRecord = {
|
||||
rev: 'foo-new',
|
||||
prev: 'foo',
|
||||
modelName: this.modelName,
|
||||
|
@ -536,7 +537,7 @@ describe('Change', function() {
|
|||
|
||||
expect(diff.conflicts, 'conflicts').to.have.length(0);
|
||||
expect(diff.deltas, 'deltas').to.have.length(1);
|
||||
var actual = diff.deltas[0].toObject();
|
||||
const actual = diff.deltas[0].toObject();
|
||||
delete actual.id;
|
||||
expect(actual).to.eql({
|
||||
checkpoint: 2,
|
||||
|
@ -551,7 +552,7 @@ describe('Change', function() {
|
|||
});
|
||||
|
||||
it('should set "prev" to local revision in remote-only delta', function(done) {
|
||||
var updateRecord = {
|
||||
const updateRecord = {
|
||||
rev: 'foo-new',
|
||||
prev: 'foo-prev',
|
||||
modelName: this.modelName,
|
||||
|
@ -565,7 +566,7 @@ describe('Change', function() {
|
|||
|
||||
expect(diff.conflicts, 'conflicts').to.have.length(0);
|
||||
expect(diff.deltas, 'deltas').to.have.length(1);
|
||||
var actual = diff.deltas[0].toObject();
|
||||
const actual = diff.deltas[0].toObject();
|
||||
delete actual.id;
|
||||
expect(actual).to.eql({
|
||||
checkpoint: 2,
|
||||
|
@ -580,7 +581,7 @@ describe('Change', function() {
|
|||
});
|
||||
|
||||
it('should set "prev" to null for a new instance', function(done) {
|
||||
var updateRecord = {
|
||||
const updateRecord = {
|
||||
rev: 'new-rev',
|
||||
prev: 'new-prev',
|
||||
modelName: this.modelName,
|
||||
|
@ -593,7 +594,7 @@ describe('Change', function() {
|
|||
|
||||
expect(diff.conflicts).to.have.length(0);
|
||||
expect(diff.deltas).to.have.length(1);
|
||||
var actual = diff.deltas[0].toObject();
|
||||
const actual = diff.deltas[0].toObject();
|
||||
delete actual.id;
|
||||
expect(actual).to.eql({
|
||||
checkpoint: 2,
|
||||
|
@ -613,7 +614,7 @@ describe('Change with with custom properties', function() {
|
|||
let Change, TestModel;
|
||||
|
||||
beforeEach(function() {
|
||||
let memory = loopback.createDataSource({
|
||||
const memory = loopback.createDataSource({
|
||||
connector: loopback.Memory,
|
||||
});
|
||||
|
||||
|
@ -629,7 +630,7 @@ describe('Change with with custom properties', function() {
|
|||
this.modelName = TestModel.modelName;
|
||||
|
||||
TestModel.prototype.fillCustomChangeProperties = function(change, cb) {
|
||||
var inst = this;
|
||||
const inst = this;
|
||||
|
||||
if (inst && inst.tenantId) {
|
||||
change.tenantId = inst.tenantId;
|
||||
|
|
|
@ -1,19 +1,19 @@
|
|||
// Copyright IBM Corp. 2014,2016. All Rights Reserved.
|
||||
// Copyright IBM Corp. 2014,2019. All Rights Reserved.
|
||||
// Node module: loopback
|
||||
// This file is licensed under the MIT License.
|
||||
// License text available at https://opensource.org/licenses/MIT
|
||||
|
||||
'use strict';
|
||||
var async = require('async');
|
||||
var loopback = require('../');
|
||||
var expect = require('./helpers/expect');
|
||||
const async = require('async');
|
||||
const loopback = require('../');
|
||||
const expect = require('./helpers/expect');
|
||||
|
||||
var Checkpoint = loopback.Checkpoint.extend('TestCheckpoint');
|
||||
const Checkpoint = loopback.Checkpoint.extend('TestCheckpoint');
|
||||
|
||||
describe('Checkpoint', function() {
|
||||
describe('bumpLastSeq() and current()', function() {
|
||||
beforeEach(function() {
|
||||
var memory = loopback.createDataSource({
|
||||
const memory = loopback.createDataSource({
|
||||
connector: loopback.Memory,
|
||||
});
|
||||
Checkpoint.attachTo(memory);
|
||||
|
@ -78,13 +78,13 @@ describe('Checkpoint', function() {
|
|||
});
|
||||
|
||||
it('Checkpoint.current() for non existing checkpoint should initialize checkpoint',
|
||||
function(done) {
|
||||
Checkpoint.current(function(err, seq) {
|
||||
expect(seq).to.equal(1);
|
||||
function(done) {
|
||||
Checkpoint.current(function(err, seq) {
|
||||
expect(seq).to.equal(1);
|
||||
|
||||
done(err);
|
||||
done(err);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('bumpLastSeq() works when singleton instance does not exists yet', function(done) {
|
||||
Checkpoint.bumpLastSeq(function(err, cp) {
|
||||
|
|
|
@ -1,16 +1,16 @@
|
|||
// Copyright IBM Corp. 2013,2016. All Rights Reserved.
|
||||
// Copyright IBM Corp. 2016,2019. All Rights Reserved.
|
||||
// Node module: loopback
|
||||
// This file is licensed under the MIT License.
|
||||
// License text available at https://opensource.org/licenses/MIT
|
||||
|
||||
'use strict';
|
||||
|
||||
var expect = require('chai').expect;
|
||||
var loopback = require('..');
|
||||
var supertest = require('supertest');
|
||||
const expect = require('chai').expect;
|
||||
const loopback = require('..');
|
||||
const supertest = require('supertest');
|
||||
|
||||
describe('OptionsFromRemotingContext', function() {
|
||||
var app, request, accessToken, userId, Product, actualOptions;
|
||||
let app, request, accessToken, userId, Product, actualOptions;
|
||||
|
||||
beforeEach(setupAppAndRequest);
|
||||
beforeEach(resetActualOptions);
|
||||
|
@ -133,7 +133,7 @@ describe('OptionsFromRemotingContext', function() {
|
|||
// despite the fact that under the hood a method on "modelTo" is called.
|
||||
|
||||
context('hasManyThrough', function() {
|
||||
var Category, ThroughModel;
|
||||
let Category, ThroughModel;
|
||||
|
||||
beforeEach(givenCategoryHasManyProductsThroughAnotherModel);
|
||||
beforeEach(givenCategoryAndProduct);
|
||||
|
@ -215,7 +215,8 @@ describe('OptionsFromRemotingContext', function() {
|
|||
Category = app.registry.createModel(
|
||||
'Category',
|
||||
{name: String},
|
||||
{forceId: false, replaceOnPUT: true});
|
||||
{forceId: false, replaceOnPUT: true},
|
||||
);
|
||||
|
||||
app.model(Category, {dataSource: 'db'});
|
||||
// This is a shortcut for creating CategoryProduct "through" model
|
||||
|
@ -241,7 +242,7 @@ describe('OptionsFromRemotingContext', function() {
|
|||
});
|
||||
|
||||
context('hasOne', function() {
|
||||
var Category;
|
||||
let Category;
|
||||
|
||||
beforeEach(givenCategoryHasOneProduct);
|
||||
beforeEach(givenCategoryId1);
|
||||
|
@ -287,7 +288,8 @@ describe('OptionsFromRemotingContext', function() {
|
|||
Category = app.registry.createModel(
|
||||
'Category',
|
||||
{name: String},
|
||||
{forceId: false, replaceOnPUT: true});
|
||||
{forceId: false, replaceOnPUT: true},
|
||||
);
|
||||
|
||||
app.model(Category, {dataSource: 'db'});
|
||||
Category.hasOne(Product);
|
||||
|
@ -311,7 +313,7 @@ describe('OptionsFromRemotingContext', function() {
|
|||
});
|
||||
|
||||
context('belongsTo', function() {
|
||||
var Category;
|
||||
let Category;
|
||||
|
||||
beforeEach(givenCategoryBelongsToProduct);
|
||||
|
||||
|
@ -331,7 +333,8 @@ describe('OptionsFromRemotingContext', function() {
|
|||
Category = app.registry.createModel(
|
||||
'Category',
|
||||
{name: String},
|
||||
{forceId: false, replaceOnPUT: true});
|
||||
{forceId: false, replaceOnPUT: true},
|
||||
);
|
||||
|
||||
app.model(Category, {dataSource: 'db'});
|
||||
Category.belongsTo(Product);
|
||||
|
@ -361,7 +364,8 @@ describe('OptionsFromRemotingContext', function() {
|
|||
Product = app.registry.createModel(
|
||||
'Product',
|
||||
{name: String},
|
||||
{forceId: false, replaceOnPUT: true});
|
||||
{forceId: false, replaceOnPUT: true},
|
||||
);
|
||||
|
||||
Product.createOptionsFromRemotingContext = function(ctx) {
|
||||
return {injectedFrom: 'Product'};
|
||||
|
@ -378,7 +382,7 @@ describe('OptionsFromRemotingContext', function() {
|
|||
}
|
||||
|
||||
function observeOptionsBeforeSave() {
|
||||
var Model = arguments[0] || Product;
|
||||
const Model = arguments[0] || Product;
|
||||
Model.observe('before save', function(ctx, next) {
|
||||
actualOptions = ctx.options;
|
||||
next();
|
||||
|
@ -386,7 +390,7 @@ describe('OptionsFromRemotingContext', function() {
|
|||
}
|
||||
|
||||
function observeOptionsBeforeDelete() {
|
||||
var Model = arguments[0] || Product;
|
||||
const Model = arguments[0] || Product;
|
||||
Model.observe('before delete', function(ctx, next) {
|
||||
actualOptions = ctx.options;
|
||||
next();
|
||||
|
@ -394,7 +398,7 @@ describe('OptionsFromRemotingContext', function() {
|
|||
}
|
||||
|
||||
function observeOptionsOnAccess() {
|
||||
var Model = arguments[0] || Product;
|
||||
const Model = arguments[0] || Product;
|
||||
Model.observe('access', function(ctx, next) {
|
||||
actualOptions = ctx.options;
|
||||
next();
|
||||
|
|
|
@ -1,14 +1,14 @@
|
|||
// Copyright IBM Corp. 2013,2016. All Rights Reserved.
|
||||
// Copyright IBM Corp. 2013,2019. All Rights Reserved.
|
||||
// Node module: loopback
|
||||
// This file is licensed under the MIT License.
|
||||
// License text available at https://opensource.org/licenses/MIT
|
||||
|
||||
'use strict';
|
||||
var assert = require('assert');
|
||||
var loopback = require('../');
|
||||
const assert = require('assert');
|
||||
const loopback = require('../');
|
||||
|
||||
describe('DataSource', function() {
|
||||
var memory;
|
||||
let memory;
|
||||
|
||||
beforeEach(function() {
|
||||
memory = loopback.createDataSource({
|
||||
|
@ -20,7 +20,7 @@ describe('DataSource', function() {
|
|||
|
||||
describe('dataSource.createModel(name, properties, settings)', function() {
|
||||
it('Define a model and attach it to a `DataSource`', function() {
|
||||
var Color = memory.createModel('color', {name: String});
|
||||
const Color = memory.createModel('color', {name: String});
|
||||
assert.isFunc(Color, 'find');
|
||||
assert.isFunc(Color, 'findById');
|
||||
assert.isFunc(Color, 'findOne');
|
||||
|
@ -45,31 +45,31 @@ describe('DataSource', function() {
|
|||
});
|
||||
|
||||
it('should honor settings.base', function() {
|
||||
var Base = memory.createModel('base');
|
||||
var Color = memory.createModel('color', {name: String}, {base: Base});
|
||||
const Base = memory.createModel('base');
|
||||
const Color = memory.createModel('color', {name: String}, {base: Base});
|
||||
assert(Color.prototype instanceof Base);
|
||||
assert.equal(Color.base, Base);
|
||||
});
|
||||
|
||||
it('should use loopback.PersistedModel as the base for DBs', function() {
|
||||
var Color = memory.createModel('color', {name: String});
|
||||
const Color = memory.createModel('color', {name: String});
|
||||
assert(Color.prototype instanceof loopback.PersistedModel);
|
||||
assert.equal(Color.base, loopback.PersistedModel);
|
||||
});
|
||||
|
||||
it('should use loopback.Model as the base for non DBs', function() {
|
||||
// Mock up a non-DB connector
|
||||
var Connector = function() {
|
||||
const Connector = function() {
|
||||
};
|
||||
Connector.prototype.getTypes = function() {
|
||||
return ['rest'];
|
||||
};
|
||||
|
||||
var ds = loopback.createDataSource({
|
||||
const ds = loopback.createDataSource({
|
||||
connector: new Connector(),
|
||||
});
|
||||
|
||||
var Color = ds.createModel('color', {name: String});
|
||||
const Color = ds.createModel('color', {name: String});
|
||||
assert(Color.prototype instanceof Color.registry.getModel('Model'));
|
||||
assert.equal(Color.base.modelName, 'PersistedModel');
|
||||
});
|
||||
|
@ -77,7 +77,7 @@ describe('DataSource', function() {
|
|||
|
||||
describe.skip('PersistedModel Methods', function() {
|
||||
it('List the enabled and disabled methods', function() {
|
||||
var TestModel = loopback.PersistedModel.extend('TestPersistedModel');
|
||||
const TestModel = loopback.PersistedModel.extend('TestPersistedModel');
|
||||
TestModel.attachTo(loopback.memory());
|
||||
|
||||
// assert the defaults
|
||||
|
@ -109,9 +109,9 @@ describe('DataSource', function() {
|
|||
existsAndShared('reload', false);
|
||||
|
||||
function existsAndShared(Model, name, isRemoteEnabled, isProto) {
|
||||
var scope = isProto ? Model.prototype : Model;
|
||||
var fn = scope[name];
|
||||
var actuallyEnabled = Model.getRemoteMethod(name);
|
||||
const scope = isProto ? Model.prototype : Model;
|
||||
const fn = scope[name];
|
||||
const actuallyEnabled = Model.getRemoteMethod(name);
|
||||
assert(fn, name + ' should be defined!');
|
||||
assert(actuallyEnabled === isRemoteEnabled,
|
||||
name + ' ' + (isRemoteEnabled ? 'should' : 'should not') +
|
||||
|
@ -121,7 +121,7 @@ describe('DataSource', function() {
|
|||
});
|
||||
});
|
||||
|
||||
var assertValidDataSource = function(dataSource) {
|
||||
function assertValidDataSource(dataSource) {
|
||||
// has methods
|
||||
assert.isFunc(dataSource, 'createModel');
|
||||
assert.isFunc(dataSource, 'discoverModelDefinitions');
|
||||
|
@ -130,7 +130,7 @@ var assertValidDataSource = function(dataSource) {
|
|||
assert.isFunc(dataSource, 'disableRemote');
|
||||
assert.isFunc(dataSource, 'defineOperation');
|
||||
assert.isFunc(dataSource, 'operations');
|
||||
};
|
||||
}
|
||||
|
||||
assert.isFunc = function(obj, name) {
|
||||
assert(obj, 'cannot assert function ' + name + ' on object that doesnt exist');
|
||||
|
|
|
@ -1,19 +1,19 @@
|
|||
// Copyright IBM Corp. 2014,2016. All Rights Reserved.
|
||||
// Copyright IBM Corp. 2014,2019. All Rights Reserved.
|
||||
// Node module: loopback
|
||||
// This file is licensed under the MIT License.
|
||||
// License text available at https://opensource.org/licenses/MIT
|
||||
|
||||
'use strict';
|
||||
var path = require('path');
|
||||
var loopback = require('../../');
|
||||
var models = require('../fixtures/e2e/models');
|
||||
var TestModel = models.TestModel;
|
||||
var assert = require('assert');
|
||||
const path = require('path');
|
||||
const loopback = require('../../');
|
||||
const models = require('../fixtures/e2e/models');
|
||||
const TestModel = models.TestModel;
|
||||
const assert = require('assert');
|
||||
|
||||
describe('RemoteConnector', function() {
|
||||
before(function() {
|
||||
// setup the remote connector
|
||||
var ds = loopback.createDataSource({
|
||||
const ds = loopback.createDataSource({
|
||||
url: 'http://127.0.0.1:3000/api',
|
||||
connector: loopback.Remote,
|
||||
});
|
||||
|
@ -33,7 +33,7 @@ describe('RemoteConnector', function() {
|
|||
});
|
||||
|
||||
it('should be able to call save', function(done) {
|
||||
var m = new TestModel({
|
||||
const m = new TestModel({
|
||||
foo: 'bar',
|
||||
});
|
||||
m.save(function(err, data) {
|
||||
|
|
|
@ -1,32 +1,32 @@
|
|||
// Copyright IBM Corp. 2014,2016. All Rights Reserved.
|
||||
// Copyright IBM Corp. 2014,2019. All Rights Reserved.
|
||||
// Node module: loopback
|
||||
// This file is licensed under the MIT License.
|
||||
// License text available at https://opensource.org/licenses/MIT
|
||||
|
||||
'use strict';
|
||||
var path = require('path');
|
||||
var loopback = require('../../');
|
||||
var models = require('../fixtures/e2e/models');
|
||||
var TestModel = models.TestModel;
|
||||
var LocalTestModel = TestModel.extend('LocalTestModel', {}, {
|
||||
const path = require('path');
|
||||
const loopback = require('../../');
|
||||
const models = require('../fixtures/e2e/models');
|
||||
const TestModel = models.TestModel;
|
||||
const LocalTestModel = TestModel.extend('LocalTestModel', {}, {
|
||||
trackChanges: true,
|
||||
});
|
||||
var assert = require('assert');
|
||||
const assert = require('assert');
|
||||
|
||||
describe('Replication', function() {
|
||||
before(function() {
|
||||
// setup the remote connector
|
||||
var ds = loopback.createDataSource({
|
||||
const ds = loopback.createDataSource({
|
||||
url: 'http://127.0.0.1:3000/api',
|
||||
connector: loopback.Remote,
|
||||
});
|
||||
TestModel.attachTo(ds);
|
||||
var memory = loopback.memory();
|
||||
const memory = loopback.memory();
|
||||
LocalTestModel.attachTo(memory);
|
||||
});
|
||||
|
||||
it('should replicate local data to the remote', function(done) {
|
||||
var RANDOM = Math.random();
|
||||
const RANDOM = Math.random();
|
||||
|
||||
LocalTestModel.create({
|
||||
n: RANDOM,
|
||||
|
|
|
@ -1,38 +1,38 @@
|
|||
// Copyright IBM Corp. 2013,2016. All Rights Reserved.
|
||||
// Copyright IBM Corp. 2013,2019. All Rights Reserved.
|
||||
// Node module: loopback
|
||||
// This file is licensed under the MIT License.
|
||||
// License text available at https://opensource.org/licenses/MIT
|
||||
|
||||
'use strict';
|
||||
var loopback = require('../');
|
||||
var MyEmail;
|
||||
var assert = require('assert');
|
||||
var MailConnector = require('../lib/connectors/mail');
|
||||
const loopback = require('../');
|
||||
let MyEmail;
|
||||
const assert = require('assert');
|
||||
const MailConnector = require('../lib/connectors/mail');
|
||||
|
||||
describe('Email connector', function() {
|
||||
it('should set up SMTP', function() {
|
||||
var connector = new MailConnector({transports: [
|
||||
const connector = new MailConnector({transports: [
|
||||
{type: 'smtp', service: 'gmail'},
|
||||
]});
|
||||
assert(connector.transportForName('smtp'));
|
||||
});
|
||||
|
||||
it('should set up DIRECT', function() {
|
||||
var connector = new MailConnector({transports: [
|
||||
const connector = new MailConnector({transports: [
|
||||
{type: 'direct', name: 'localhost'},
|
||||
]});
|
||||
assert(connector.transportForName('direct'));
|
||||
});
|
||||
|
||||
it('should set up STUB', function() {
|
||||
var connector = new MailConnector({transports: [
|
||||
const connector = new MailConnector({transports: [
|
||||
{type: 'stub', service: 'gmail'},
|
||||
]});
|
||||
assert(connector.transportForName('stub'));
|
||||
});
|
||||
|
||||
it('should set up a single transport for SMTP', function() {
|
||||
var connector = new MailConnector({transport:
|
||||
const connector = new MailConnector({transport:
|
||||
{type: 'smtp', service: 'gmail'},
|
||||
});
|
||||
|
||||
|
@ -40,7 +40,7 @@ describe('Email connector', function() {
|
|||
});
|
||||
|
||||
it('should set up a aliased transport for SMTP', function() {
|
||||
var connector = new MailConnector({transport:
|
||||
const connector = new MailConnector({transport:
|
||||
{type: 'smtp', service: 'ses-us-east-1', alias: 'ses-smtp'},
|
||||
});
|
||||
|
||||
|
@ -51,7 +51,7 @@ describe('Email connector', function() {
|
|||
describe('Email and SMTP', function() {
|
||||
beforeEach(function() {
|
||||
MyEmail = loopback.Email.extend('my-email');
|
||||
var ds = loopback.createDataSource('email', {
|
||||
const ds = loopback.createDataSource('email', {
|
||||
connector: loopback.Mail,
|
||||
transports: [{type: 'STUB'}],
|
||||
});
|
||||
|
@ -65,7 +65,7 @@ describe('Email and SMTP', function() {
|
|||
|
||||
describe('MyEmail', function() {
|
||||
it('MyEmail.send(options, callback)', function(done) {
|
||||
var options = {
|
||||
const options = {
|
||||
to: 'to@to.com',
|
||||
from: 'from@from.com',
|
||||
subject: 'subject',
|
||||
|
@ -84,7 +84,7 @@ describe('Email and SMTP', function() {
|
|||
});
|
||||
|
||||
it('myEmail.send(callback)', function(done) {
|
||||
var message = new MyEmail({
|
||||
const message = new MyEmail({
|
||||
to: 'to@to.com',
|
||||
from: 'from@from.com',
|
||||
subject: 'subject',
|
||||
|
|
|
@ -1,14 +1,14 @@
|
|||
// Copyright IBM Corp. 2015,2016. All Rights Reserved.
|
||||
// Copyright IBM Corp. 2015,2019. All Rights Reserved.
|
||||
// Node module: loopback
|
||||
// This file is licensed under the MIT License.
|
||||
// License text available at https://opensource.org/licenses/MIT
|
||||
|
||||
'use strict';
|
||||
var loopback = require('../');
|
||||
var app;
|
||||
var assert = require('assert');
|
||||
var request = require('supertest');
|
||||
var expect = require('./helpers/expect');
|
||||
const loopback = require('../');
|
||||
let app;
|
||||
const assert = require('assert');
|
||||
const request = require('supertest');
|
||||
const expect = require('./helpers/expect');
|
||||
|
||||
describe('loopback.errorHandler(options)', function() {
|
||||
it('should throw a descriptive error', function() {
|
||||
|
|
|
@ -1,20 +1,20 @@
|
|||
// Copyright IBM Corp. 2015,2016. All Rights Reserved.
|
||||
// Copyright IBM Corp. 2015,2019. All Rights Reserved.
|
||||
// Node module: loopback
|
||||
// This file is licensed under the MIT License.
|
||||
// License text available at https://opensource.org/licenses/MIT
|
||||
|
||||
'use strict';
|
||||
var loopback = require('../../../..');
|
||||
var boot = require('loopback-boot');
|
||||
var app = module.exports = loopback({
|
||||
const loopback = require('../../../..');
|
||||
const boot = require('loopback-boot');
|
||||
const app = module.exports = loopback({
|
||||
localRegistry: true,
|
||||
loadBuiltinModels: true,
|
||||
});
|
||||
var errorHandler = require('strong-error-handler');
|
||||
const errorHandler = require('strong-error-handler');
|
||||
|
||||
boot(app, __dirname);
|
||||
|
||||
var apiPath = '/api';
|
||||
const apiPath = '/api';
|
||||
app.use(loopback.token({model: app.models.accessToken}));
|
||||
app.use(apiPath, loopback.rest());
|
||||
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
// Copyright IBM Corp. 2015,2016. All Rights Reserved.
|
||||
// Copyright IBM Corp. 2015,2019. All Rights Reserved.
|
||||
// Node module: loopback
|
||||
// This file is licensed under the MIT License.
|
||||
// License text available at https://opensource.org/licenses/MIT
|
||||
|
||||
'use strict';
|
||||
var loopback = require('../../../../index');
|
||||
var PersistedModel = loopback.PersistedModel;
|
||||
const loopback = require('../../../../index');
|
||||
const PersistedModel = loopback.PersistedModel;
|
||||
|
||||
exports.TestModel = PersistedModel.extend('TestModel', {}, {
|
||||
trackChanges: true,
|
||||
|
|
|
@ -1,15 +1,15 @@
|
|||
// Copyright IBM Corp. 2015. All Rights Reserved.
|
||||
// Copyright IBM Corp. 2015,2019. All Rights Reserved.
|
||||
// Node module: loopback
|
||||
// This file is licensed under the MIT License.
|
||||
// License text available at https://opensource.org/licenses/MIT
|
||||
|
||||
'use strict';
|
||||
var loopback = require('../../../../index');
|
||||
var app = module.exports = loopback({localRegistry: true});
|
||||
var models = require('./models');
|
||||
var TestModel = models.TestModel;
|
||||
const loopback = require('../../../../index');
|
||||
const app = module.exports = loopback({localRegistry: true});
|
||||
const models = require('./models');
|
||||
const TestModel = models.TestModel;
|
||||
|
||||
var apiPath = '/api';
|
||||
const apiPath = '/api';
|
||||
app.use(apiPath, loopback.rest());
|
||||
|
||||
TestModel.attachTo(loopback.memory());
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// Copyright IBM Corp. 2015. All Rights Reserved.
|
||||
// Copyright IBM Corp. 2015,2018. All Rights Reserved.
|
||||
// Node module: loopback
|
||||
// This file is licensed under the MIT License.
|
||||
// License text available at https://opensource.org/licenses/MIT
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
// Copyright IBM Corp. 2015. All Rights Reserved.
|
||||
// Copyright IBM Corp. 2015,2019. All Rights Reserved.
|
||||
// Node module: loopback
|
||||
// This file is licensed under the MIT License.
|
||||
// License text available at https://opensource.org/licenses/MIT
|
||||
|
||||
'use strict';
|
||||
var boot = require('loopback-boot');
|
||||
var loopback = require('../../../../../index');
|
||||
const boot = require('loopback-boot');
|
||||
const loopback = require('../../../../../index');
|
||||
|
||||
var app = module.exports = loopback();
|
||||
const app = module.exports = loopback();
|
||||
boot(app, __dirname);
|
||||
app.use(loopback.rest());
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// Copyright IBM Corp. 2015. All Rights Reserved.
|
||||
// Copyright IBM Corp. 2015,2018. All Rights Reserved.
|
||||
// Node module: loopback
|
||||
// This file is licensed under the MIT License.
|
||||
// License text available at https://opensource.org/licenses/MIT
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
// Copyright IBM Corp. 2015. All Rights Reserved.
|
||||
// Copyright IBM Corp. 2015,2019. All Rights Reserved.
|
||||
// Node module: loopback
|
||||
// This file is licensed under the MIT License.
|
||||
// License text available at https://opensource.org/licenses/MIT
|
||||
|
||||
'use strict';
|
||||
var boot = require('loopback-boot');
|
||||
var loopback = require('../../../../../index');
|
||||
const boot = require('loopback-boot');
|
||||
const loopback = require('../../../../../index');
|
||||
|
||||
var app = module.exports = loopback();
|
||||
const app = module.exports = loopback();
|
||||
boot(app, __dirname);
|
||||
app.use(loopback.rest());
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// Copyright IBM Corp. 2015. All Rights Reserved.
|
||||
// Copyright IBM Corp. 2015,2018. All Rights Reserved.
|
||||
// Node module: loopback
|
||||
// This file is licensed under the MIT License.
|
||||
// License text available at https://opensource.org/licenses/MIT
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
// Copyright IBM Corp. 2015. All Rights Reserved.
|
||||
// Copyright IBM Corp. 2015,2019. All Rights Reserved.
|
||||
// Node module: loopback
|
||||
// This file is licensed under the MIT License.
|
||||
// License text available at https://opensource.org/licenses/MIT
|
||||
|
||||
'use strict';
|
||||
var boot = require('loopback-boot');
|
||||
var loopback = require('../../../../../index');
|
||||
const boot = require('loopback-boot');
|
||||
const loopback = require('../../../../../index');
|
||||
|
||||
var app = module.exports = loopback();
|
||||
const app = module.exports = loopback();
|
||||
boot(app, __dirname);
|
||||
app.use(loopback.rest());
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// Copyright IBM Corp. 2015. All Rights Reserved.
|
||||
// Copyright IBM Corp. 2015,2018. All Rights Reserved.
|
||||
// Node module: loopback
|
||||
// This file is licensed under the MIT License.
|
||||
// License text available at https://opensource.org/licenses/MIT
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
// Copyright IBM Corp. 2015. All Rights Reserved.
|
||||
// Copyright IBM Corp. 2015,2019. All Rights Reserved.
|
||||
// Node module: loopback
|
||||
// This file is licensed under the MIT License.
|
||||
// License text available at https://opensource.org/licenses/MIT
|
||||
|
||||
'use strict';
|
||||
var boot = require('loopback-boot');
|
||||
var loopback = require('../../../../../index');
|
||||
const boot = require('loopback-boot');
|
||||
const loopback = require('../../../../../index');
|
||||
|
||||
var app = module.exports = loopback();
|
||||
const app = module.exports = loopback();
|
||||
boot(app, __dirname);
|
||||
app.use(loopback.rest());
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue