Compare commits

...

213 Commits

Author SHA1 Message Date
Miroslav Bajtoš f8c5b257f0
2.57.0
* Add support for Node.js 8.x and 10.x, drop 4.x (Miroslav Bajtoš)
2018-05-18 18:18:56 +02:00
Miroslav Bajtoš cc685989a3
Merge pull request #1584 from strongloop/fix/supported-node-versions
Add support for Node.js 8.x and 10.x, drop 4.x [2.x]
2018-05-18 18:18:14 +02:00
Miroslav Bajtoš b056255ecd
Add support for Node.js 8.x and 10.x, drop 4.x
Also remove a flaky test assertion that reports different results
on different Node.js versions. This assertion has been already
removed on `master` branch (3.x version line).
2018-05-18 17:26:47 +02:00
Kevin Delisle 9917e1d283 2.56.0
* fix unauthorized fk change (#1540) (Taranveer Virk)
 * drop support for node 0.10 / 0.12 (#1541) (Taranveer Virk)
2018-01-19 12:55:59 -05:00
Taranveer Virk 2167e802a9
fix unauthorized fk change (#1540) 2018-01-19 12:48:56 -05:00
Taranveer Virk b26da798a9
drop support for node 0.10 / 0.12 (#1541)
Drop support for Node 0.10/0.12 as it’s not supported by Node and tests
are failing since our dependencies no longer support Node 0.10/0.12
2018-01-19 11:28:31 -05:00
Kevin Delisle 60a47b4439 2.55.3
* fix(model-builder): __data may be null. (#1415) (Samuel Reed)
 * package: use qs@6.5.0 (#1470) (Kevin Delisle)
2017-09-26 16:41:31 -04:00
Samuel Reed 40e278ab96 fix(model-builder): __data may be null. (#1415)
This error was swallowed by strong-remoting try/catch
in eachRemoteFunctionInObject()
2017-09-26 16:37:51 -04:00
Kevin Delisle 7c196af109 package: use qs@6.5.0 (#1470)
Fix for advisory: https://snyk.io/vuln/npm:qs:20170213
2017-08-23 15:16:45 -04:00
Kevin Delisle 83d9b865d5 2.55.2
* Fix #1434 2.55.1 Throws when using where query against "JSON Object or ANY" Type (#1444) (Shing)
 * castPropertyValue: throw on malformed types (Kevin Delisle)
2017-07-28 16:33:46 -04:00
Shing 88771f10fc Fix #1434 2.55.1 Throws when using where query against "JSON Object or ANY" Type (#1444)
* Test for JSON object or any type

* dao.js coercion type.definition existence check
2017-07-28 16:30:49 -04:00
Kevin Delisle 29676f6510 Merge pull request #1441 from strongloop/memory/throw-on-malformed-types
castPropertyValue: throw on malformed types
2017-07-28 16:26:16 -04:00
Kevin Delisle f3b849bb47 castPropertyValue: throw on malformed types 2017-07-28 12:50:51 -04:00
Kevin Delisle e63bbcf9cd 2.55.1
* Catch errors using cb (loay)
 * Recursively cast props on fromDb for memory conn. (nVitius)
 * Recursively coerce nested properties in dao (nVitius)
 * Fix #1079 - Polymorphic hasMany inverse relation (#1296) (Janny)
2017-07-21 14:12:10 -04:00
Loay 9096e0873e Merge pull request #1433 from strongloop/create-update-pass
Catch errors using cb
2017-07-21 12:27:48 -04:00
loay 63f04a28d4 Catch errors using cb 2017-07-21 10:13:16 -04:00
Sakib Hasan 1d8ca908f6 Merge pull request #1361 from nVitius/Fix/2.x/NestedProperties
Fix/2.x/nested properties
2017-07-13 21:34:01 -04:00
nVitius 3df1826730 Recursively cast props on fromDb for memory conn.
In the fromDb step, nested properties weren't being hydrated properly.
This caused an issue with the ability to search on such properties as
Dates. The properties would be hydrated as a String type, and as such,
it was impossible to properly query on them.
2017-07-12 12:51:19 -07:00
nVitius 8ebccb65d1 Recursively coerce nested properties in dao 2017-07-11 12:55:01 -07:00
Janny 6d017c1e34 Fix #1079 - Polymorphic hasMany inverse relation (#1296)
This fixes the polymorphic hasMany inverse relation
not working as expected in include filters.
2017-07-11 15:47:32 -04:00
Kevin Delisle da323964f5 2.55.0
* Backport - Apply hasManyThrough filter on target model (#1404) (Janny)
2017-07-10 16:39:03 -04:00
Janny 2190f2510f Backport - Apply hasManyThrough filter on target model (#1404)
* Apply hasManyThrough filter on target model

* Compatible with mongodb
2017-06-22 16:51:10 -04:00
Kevin Delisle 5a661a064a 2.54.2
* Fix assert as array does not guarantee order (Candy)
 * Fixed empty objList in linkOneToMany fn (#1287) (somename85)
 * remove equality value for user-defined id (#1292) (Matteo Padovano)
 * override collection name for arangodb (#1274) (Matteo Padovano)
 * fix missing findOrCreate error callback (Ryan Graham)
2017-06-13 12:37:24 -04:00
Candy 4ebc421c11 Merge pull request #1367 from strongloop/fix_testcase_order
Fix assert as array does not guarantee order
2017-05-23 13:45:42 -04:00
Candy 3e02e36314 Fix assert as array does not guarantee order 2017-05-05 16:14:17 -04:00
somename85 cbb3f6f98a Fixed empty objList in linkOneToMany fn (#1287) 2017-04-05 10:27:34 -04:00
Matteo Padovano 93b89f3a51 remove equality value for user-defined id (#1292)
Some connector, like arangodb, not support id as Number.
 When `forceId` is set to true and id is set `Model.isValid`
 report error and the field id is not coerced.
2017-04-04 13:15:19 -04:00
Matteo Padovano 570da781b8 override collection name for arangodb (#1274)
Add collection name settings for arangodb database.
Required to pass common tests included in community connector
2017-03-25 18:22:59 -04:00
Ryan Graham e9baea77e0
fix missing findOrCreate error callback
Closes #1226
Resolves #1223

Signed-off-by: Ryan Graham <r.m.graham@gmail.com>
2017-03-16 18:32:47 -07:00
Miroslav Bajtoš 7d45463a22
2.54.1
* fix incorrect engines property in package.json (Ryan Graham)
 * Fix datasource to report connector-loading errors (Miroslav Bajtoš)
 * Add two basic tests for "inq" operator (Miroslav Bajtoš)
2017-03-13 16:16:57 +01:00
Ryan Graham f7af629e96 Merge pull request #1263 from strongloop/fix-engines-2.x
fix incorrect engines property in package.json
2017-03-03 13:08:59 -08:00
Ryan Graham 5cbc3bfae7
fix incorrect engines property in package.json 2017-03-03 12:56:56 -08:00
Miroslav Bajtoš e7274a39dc Merge pull request #1246 from strongloop/backport/fix-connector-loader
Fix datasource to report connector-loading errors
2017-02-06 11:51:22 +01:00
Miroslav Bajtoš af1d959613 Fix datasource to report connector-loading errors
Before this change, when resolving full connector path, all errors were
ignored. As a result, when the connector was installed but not
correctly built (e.g. loopback-connector-db2 which uses a native addon),
a very confusing message was reported by LoopBack.

In this commit, I am fixing the code handling `require()` errors
to ignore only MODULE_NOT_FOUND errors that contain the name
of the required module.
2017-02-06 10:58:44 +01:00
Miroslav Bajtoš cbc8c8efe0 Merge pull request #1234 from strongloop/backport/add-inq-tests
Add two basic tests for "inq" operator
2017-01-20 13:20:49 +01:00
Miroslav Bajtoš 5492e59207 Add two basic tests for "inq" operator 2017-01-20 13:04:28 +01:00
Raymond Feng 26d25d60fd 2.54.0
* Replace deprecated node-uuid with uuid module (Raymond Feng)
 * Coerce array-like objects into arrays (Heath Morrison)
 * Throw error when model relation name is trigger (Brian Schemp)
 * Update package.json for LB3 release (Simon Ho)
2017-01-17 09:29:46 -08:00
Raymond Feng e251f05914 Merge pull request #1227 from strongloop/replace-node-uuid
Replace deprecated node-uuid with uuid module
2017-01-17 09:28:54 -08:00
Raymond Feng 9bfab2b5a2 Replace deprecated node-uuid with uuid module 2017-01-17 09:07:21 -08:00
Miroslav Bajtoš 485ad3ee78 Merge pull request #1219 from strongloop/backport/array-coercion
Coerce array-like objects into arrays
2017-01-10 16:05:13 +01:00
Heath Morrison c62ba21307 Coerce array-like objects into arrays
The query-string parser used by express
https://github.com/ljharb/qs#parsing-arrays
limits the size of arrays that are created from query strings to 20
items. Arrays larger than that are converted to objects using numeric
indices.

This commit fixes the coercion algorithm used by queries to
treat number-indexed objects as arrays. We still maintain a strict
understanding of an "array-like object" to limit the opportunity for
subtle bugs. In particular, the presence of non-index keys is an
indication that the object was not intended to be interpreted as
an array.
2017-01-10 15:11:20 +01:00
Miroslav Bajtoš 553c9448ac Merge pull request #1209 from strongloop/backport/invalid-model-relation-name-2x
Throw error when model relation name is trigger
2017-01-03 11:48:53 +01:00
Brian Schemp d87e3bf8fb Throw error when model relation name is trigger
Defining a model relation with the name "trigger" causes the model not
able to insert records. No error is thrown when a model relation with
the name "trigger" is defined. Adding a check for the model relation
name "trigger" will now throw an error.
2017-01-03 10:41:07 +01:00
Miroslav Bajtoš 4544401368 Merge pull request #1199 from strongloop/release/2.x-lts
Update package.json for LB2-LTS release
2016-12-21 10:45:38 +01:00
Miroslav Bajtoš 5b836bdae0 2.53.1
* Fix HasOne.update to propagate options arg (Miroslav Bajtoš)
 * Update ko translation file (Candy)
 * Back-port fixes for linter errors from master (Miroslav Bajtoš)
 * Update chinese simplified translation file (Candy)
 * Apply style guide to test names (Amir Jafarian)
 * Continue _coerce after logical operators (Heath Morrison)
 * Fix manually (Amir Jafarian)
 * Auto-update by eslint --fix (Amir Jafarian)
 * update eslintrc according to master branch (Amir Jafarian)
 * Update eslint (Amir Jafarian)
 * test/kvao: add connectorCapabilities options (Miroslav Bajtoš)
 * Update validations.js (Rand McKinney)
 * Fix bug when near filter is used (Amir Jafarian)
2016-12-21 10:41:36 +01:00
Miroslav Bajtoš 71ef3dd095 Merge pull request #1201 from strongloop/fix/options-propagation-2x
Fix HasOne.update to propagate options arg
2016-12-21 10:40:29 +01:00
Miroslav Bajtoš 839a2c6c66 Fix HasOne.update to propagate options arg 2016-12-21 10:34:05 +01:00
Simon Ho fae9fd69c0 Update package.json for LB3 release
- Use LTS tag for Juggler@2.x
2016-12-21 00:45:16 -08:00
Simon Ho f6c054e3f4 Merge pull request #1188 from strongloop/add_translation6_2x
Update ko translation file
2016-12-07 09:49:09 -08:00
Candy 70b7b1533d Update ko translation file 2016-12-07 12:01:59 -05:00
Miroslav Bajtoš de70d746e8 Merge pull request #1183 from strongloop/fix/eslint-errors-2x
Back-port fixes for linter errors from master
2016-12-06 15:09:40 +01:00
Miroslav Bajtoš f8da9783bc Back-port fixes for linter errors from master 2016-12-06 10:47:27 +01:00
Candy a1b9b42d11 Merge pull request #1162 from strongloop/add_translation4_2x
Update chinese simplified translation file
2016-11-10 15:19:39 -05:00
Candy d7a5429f52 Update chinese simplified translation file 2016-11-10 12:55:53 -05:00
Amirali Jafarian adcebab371 Merge pull request #1157 from strongloop/1150_backport
Apply style guide to test names
2016-10-28 09:28:56 -04:00
Amir Jafarian 69ce75c662 Apply style guide to test names
*  Apply style guide to test names in test/loopback-dl.test.js
Backport of #1150
2016-10-27 18:02:26 -04:00
Amirali Jafarian 9923ddb29b Merge pull request #1151 from strongloop/backport_1142
Continue _coerce after logical operators
2016-10-26 13:51:08 -04:00
Heath Morrison 552dfc8f24 Continue _coerce after logical operators 2016-10-26 10:14:43 -04:00
Amirali Jafarian c96f471efd Merge pull request #1146 from strongloop/2.x_eslint
2.x eslint
2016-10-19 21:24:56 -04:00
Amir Jafarian e038b28e8c Fix manually 2016-10-19 17:35:26 -04:00
Amir Jafarian a1683fca59 Auto-update by eslint --fix 2016-10-19 17:04:05 -04:00
Amir Jafarian 6acedc3bae update eslintrc according to master branch 2016-10-19 17:01:49 -04:00
Amir Jafarian 342eb866be Update eslint 2016-10-19 16:59:06 -04:00
Miroslav Bajtoš 959a821be5 Merge pull request #1144 from strongloop/feature/parameterize-kvao-tests-2x
test/kvao: add connectorCapabilities options [2.x]
2016-10-19 13:40:28 +02:00
Miroslav Bajtoš 70a1552df8 test/kvao: add connectorCapabilities options
- canExpire
 - canQueryTtl
 - ttlPrecision
 - canIterateKeys
 - canIterateLargeKeySets

These options allow connectors to disable shared tests for features
that are not supported/implemented.
2016-10-19 12:33:21 +02:00
Amirali Jafarian 459fc93012 Merge pull request #1138 from strongloop/doc_validation
Update validations.js
2016-10-14 11:32:55 -04:00
Rand McKinney 09fbfe9410 Update validations.js
Add doc of allowNull options property per #372.
Back-port 9a1ef08
2016-10-14 11:18:17 -04:00
Candy e3d76f7f19 2.53.0
* Fix bug when near filter is used (Amir Jafarian)
2016-10-13 15:35:25 -04:00
Amirali Jafarian 8caf4c8327 Merge pull request #1108 from strongloop/fix_geo_2.x
Fix the bug when near filter is used
2016-10-13 10:39:15 -04:00
Miroslav Bajtoš ff184aec16 2.52.0
* Support {defaultFn: 'shortid'} (#1110) (Simon Ho)
 * Add ilike and nilike operators (#1136) (Simon Ho)
 * Fix JSdoc (Amir Jafarian)
 * Update ja translation file (Candy)
 * Update translation files - round#2 (Candy)
 * Add 'isNewInstance' for updateAttributes (Amir Jafarian)
 * Add globalization strings (Amir Jafarian)
 * Add docs for KVAO (Simon Ho)
 * Skip test temporarily (Loay)
2016-10-13 10:13:27 +02:00
Simon Ho c3eca4025c Support {defaultFn: 'shortid'} (#1110)
- Backport of strongloop/loopback-datasource-juggler#1107 (takeover of
strongloop/loopback-datasource-juggler#1101)
- Squashed relevant commits
2016-10-12 20:58:07 -07:00
Simon Ho a9d381605e Add ilike and nilike operators (#1136)
Closes #633

Backport of #1091
2016-10-12 19:19:31 -07:00
Amir Jafarian fde4c0bfd6 Fix bug when near filter is used 2016-10-11 17:08:42 -04:00
Amirali Jafarian da57136fed Merge pull request #1134 from strongloop/1132_2x
Fix JSdoc
2016-10-11 12:54:48 -04:00
Amir Jafarian 8208e8ab63 Fix JSdoc 2016-10-11 12:21:36 -04:00
Amirali Jafarian 7e8198f88b Merge pull request #1131 from strongloop/add_translation3_2x
Update ja translation file
2016-10-06 15:03:09 -04:00
Candy 35e64d2e0b Update ja translation file 2016-10-06 14:26:46 -04:00
Candy 4d965d2de1 Merge pull request #1119 from strongloop/add_translation2_2x
Update translation files - round#2
2016-09-30 09:54:03 -04:00
Candy b7266bd1cb Update translation files - round#2 2016-09-28 15:32:13 -04:00
Amirali Jafarian 76398b7f45 Merge pull request #1105 from strongloop/newInstance_updateAttributes_2.x
Add 'isNewInstance' for updateAttributes
2016-09-23 15:34:32 -04:00
Amir Jafarian e103b50b63 Add 'isNewInstance' for updateAttributes
* Add 'isNewInstance' to loaded hook for updateAttes
2016-09-23 14:09:44 -04:00
Amirali Jafarian 24eb3d3953 Merge pull request #1104 from strongloop/add_globalization_strings
Add globalization strings
2016-09-21 10:22:50 -04:00
Amir Jafarian 74312c9948 Add globalization strings 2016-09-20 15:18:49 -04:00
Simon Ho 94ca3bcd9d Merge pull request #1100 from strongloop/backport/docs-for-kvao
Backport: Add docs for KVAO
2016-09-19 15:22:39 -07:00
Simon Ho 8edca500c1 Add docs for KVAO
Backport of #1093
2016-09-19 13:45:16 -07:00
Simon Ho d84b481483 Merge pull request #1090 from strongloop/backport/fix-ci-failure-mssql
Backport Skip test temporarily
2016-09-15 11:45:01 -07:00
Loay d324696d2a Skip test temporarily 2016-09-14 22:23:24 -04:00
Miroslav Bajtoš 71d4adb0b0 2.51.0
* Add missing "done" arg in test/kvao/ttl.suite (Miroslav Bajtoš)
 * Support nested queries for arrays (pponugo)
 * Remove expired item before executing expire (Simon Ho)
 * Fix failures for connectors (Amir Jafarian)
 * upsertWithWhere feature support in juggler DAO (Sonali Samantaray)
 * Remove ESLint from dependencies (Simon Ho)
 * Refactor TTL test suite (Simon Ho)
2016-09-12 11:21:14 +02:00
Miroslav Bajtoš f663917fe9 Add missing "done" arg in test/kvao/ttl.suite
Backport #1086
2016-09-08 10:39:47 +02:00
Miroslav Bajtoš d385dfa9d6 Merge pull request #1027 from pponugo/2.x
Support nested queries for arrays

Close #1027
2016-09-07 13:26:02 +02:00
pponugo 64a9b8d506 Support nested queries for arrays
Enhance the built-in memory connector to correctly support nested
queries for arrays in addition to objects.

E.g. if "friends" is an array of objects containing "name", then
{ where: { "friends.name": "Jane" } } should match records containing
a friend called "Jane".
2016-09-07 13:20:56 +02:00
Simon Ho a9d10ebfbb Merge pull request #1074 from strongloop/backport/remove-expired-items-before-executing-expire
Remove expired item before executing expire
2016-09-06 18:41:41 -07:00
Simon Ho 95bc2e93cc Remove expired item before executing expire
The expire feature is falsely returning 204 instead of 404 because it is
not removing expired items before execution.
2016-09-06 16:04:33 -07:00
Amirali Jafarian b15681968b Merge pull request #1052 from strongloop/upsertWithWhere_2x
upsertWithWhere feature support in juggler DAO
2016-09-06 15:51:23 -04:00
Amir Jafarian 12aadd4266 Fix failures for connectors 2016-09-06 13:41:50 -04:00
Sonali Samantaray 44e1d29879 upsertWithWhere feature support in juggler DAO
Backport from #1001
2016-09-06 13:41:50 -04:00
Miroslav Bajtoš fce166245f Merge pull request #1076 from strongloop/rm-eslint-from-deps
Remove ESLint from dependencies
2016-09-05 13:37:01 +02:00
Simon Ho 25345d56eb Remove ESLint from dependencies
This a duplicate entry as the correct ESLint configs are already listed
as devDepencies.
2016-09-04 12:20:47 -07:00
Simon Ho 9afaecaada Merge pull request #1077 from strongloop/refactor/ttl-test-suite
Refactor TTL test suite
2016-09-04 12:02:42 -07:00
Simon Ho cf432fceec Refactor TTL test suite 2016-09-03 11:36:28 -07:00
Miroslav Bajtoš b95d5ca182 2.50.0
* test/memory: remove dummy findOrCreate impl (Miroslav Bajtoš)
 * Implement `notify` for find method (Amir Jafarian)
 * kvao: implement key filter (Miroslav Bajtoš)
 * kvao: add iterateKeys() and keys() (Miroslav Bajtoš)
 * Globalize KeyValue Memory connector (Simon Ho)
 * test: Rename KVAO get test suite (Simon Ho)
2016-08-26 10:08:32 +02:00
Miroslav Bajtoš f4302363d3 Merge pull request #1065 from strongloop/cleanup/memory-test-2x
test/memory: remove dummy findOrCreate impl
2016-08-26 10:06:14 +02:00
Miroslav Bajtoš 65c8ec41e0 test/memory: remove dummy findOrCreate impl
Let the operation-hook tests use the real implementation,
now that we have it in place.
2016-08-26 09:53:19 +02:00
Amirali Jafarian 0bd7270f84 Merge pull request #1061 from strongloop/notify_find_2.x
Implement notify for find method [2.x]
2016-08-23 19:41:53 -04:00
Amir Jafarian a1a9bac9ba Implement `notify` for find method 2016-08-23 19:00:50 -04:00
Simon Ho de84a8bc7d Merge pull request #1057 from strongloop/backport/globalize-kv-mem-connector
Globalize KeyValue Memory connector
2016-08-19 12:59:31 -07:00
Miroslav Bajtoš 4f76475ee9 Merge pull request #1058 from strongloop/feature/kvao-iterate-keys-2x
kvao: add iterateKeys() and keys()
2016-08-18 10:48:10 +02:00
Miroslav Bajtoš ab47ecfbab kvao: implement key filter 2016-08-18 10:30:55 +02:00
Miroslav Bajtoš 24a9a96a26 kvao: add iterateKeys() and keys()
Add a core implementation of KVAO.iterateKeys() which returns an
AsyncIterator, inspired by
 - https://github.com/tc39/proposal-async-iteration
 - https://www.npmjs.com/package/async-iterators

This way we can safely iterate even large sets of data.

Also add KVAO.keys(), a sugar API converting the result of iterateKeys()
into a single array.
2016-08-18 10:30:53 +02:00
Simon Ho 873cb4eab6 Globalize KeyValue Memory connector
[back-port of #1056]
2016-08-17 18:54:30 -07:00
Simon Ho 2c1a056ade Merge pull request #1054 from strongloop/backport/rename-get-test-suite
Rename KVAO get test suite
2016-08-16 13:43:29 -07:00
Simon Ho 5001a98135 test: Rename KVAO get test suite
[back-port of #2591]
2016-08-16 09:47:23 -07:00
Miroslav Bajtoš 2fb6a96d77 2.49.0
* Fix error message for missing global.Promise (Miroslav Bajtoš)
 * Add TTL for KeyValue related features (Simon Ho)
 * test/kvao: setup global.Promise on Node v0.10 (Miroslav Bajtoš)
 * kv-memory: fix crash in regular cleanup (Miroslav Bajtoš)
 * Backport globalization (Amir Jafarian)
2016-08-16 16:36:13 +02:00
Miroslav Bajtoš 6d4e0513a7 Merge pull request #1051 from strongloop/fix/promise-error-message
Fix error message for missing global.Promise
2016-08-16 14:44:46 +02:00
Miroslav Bajtoš f7cfe25b60 Fix error message for missing global.Promise 2016-08-16 14:38:01 +02:00
Miroslav Bajtoš df4a3643eb Merge pull request #1044 from strongloop/fix/kvao-promise
test/kvao: setup global.Promise on Node v0.10 [2.x only]
2016-08-15 14:05:20 +02:00
Simon Ho 27efed9c5f Merge pull request #1046 from strongloop/backport/ttl
Add TTL for KeyValue related features
2016-08-12 17:23:53 -07:00
Simon Ho 421597c998 Add TTL for KeyValue related features
Backport of strongloop/loopback-datasource-juggler#1030
2016-08-12 14:49:01 -07:00
Miroslav Bajtoš 9d0d41d660 test/kvao: setup global.Promise on Node v0.10
When running the tests on Node v0.10, setup `global.Promise`
with Bluebird implementation. That way we don't have to run
this setup in the test suite of each kv connector.
2016-08-12 16:34:26 +02:00
Miroslav Bajtoš d8d89bea9d Merge pull request #1041 from strongloop/fix/kv-reference-error-2x
kv-memory: fix crash in regular cleanup [2.x]
2016-08-10 14:32:08 +02:00
Miroslav Bajtoš 1750583a15 kv-memory: fix crash in regular cleanup
Fix bug in "_setupRegularCleanup()" where the interval callback
was trying to access an object that has been garbage-collected
in the meantime.
2016-08-10 14:19:05 +02:00
Amir-61 5a20ea1a08 Merge pull request #1029 from strongloop/backport-globalization
Backport globalization
2016-08-10 00:09:56 -04:00
Amir Jafarian 9da0e501da Backport globalization
Backport loopback-datasource-juggler#1022,
loopback-datasource-juggler#1024,
loopback-datasource-juggler#1026
2016-08-09 21:52:11 -04:00
Amir Jafarian b4d2d19ac6 2.48.0
* Return error if connector does not implement (Amir Jafarian)
 * kvao: return 404 when expiring unknown key (Miroslav Bajtoš)
 * Update doc for `validatesFormatOf` (Amir Jafarian)
 * Implement KeyValue API and memory connector (Miroslav Bajtoš)
 * Fix test case typo (Amir Jafarian)
 * Remove unused variables in model.js (Amir Jafarian)
 * Add test for updateOrCreate (Amir Jafarian)
 * Declare `definition` (Amir Jafarian)
 * Add test to catch invalid date property (Supasate Choochaisri)
2016-08-09 18:43:11 -04:00
Amir-61 a8a105c3ee Merge pull request #1040 from strongloop/err_connector_not_implemented_replaceById_2.x
Return error if connector does not implement
2016-08-09 18:34:51 -04:00
Amir Jafarian 1b266bcf00 Return error if connector does not implement
* Return error if connector does not implement replaceById
* This patch is without globalization

Backport loopback-datasource-juggler#1034
2016-08-09 14:44:06 -04:00
Miroslav Bajtoš 72b3179da1 Merge pull request #1037 from strongloop/fix/kvao-expire-error-status-code-2x
KeyValueAccessObject: return 404 when expiring unknown key
2016-08-09 11:09:29 +02:00
Miroslav Bajtoš 8013b6acd4 kvao: return 404 when expiring unknown key 2016-08-09 10:47:12 +02:00
Amir-61 0dee7fbec7 Merge pull request #1032 from strongloop/update_doc
Update doc for `validatesFormatOf`
2016-08-08 10:51:04 -04:00
Amir Jafarian 9557558c8f Update doc for `validatesFormatOf` 2016-08-08 10:46:47 -04:00
Miroslav Bajtoš 7f277b2563 Merge pull request #1031 from strongloop/kv-memory-2x
Implement KeyValue API and memory connector [2.x]
2016-08-08 15:12:46 +02:00
Miroslav Bajtoš 837f58f82d Implement KeyValue API and memory connector
Models attached to a KeyValue connector get the following *static*
methods:

    Color.set(key, value);
    Color.set(key, value, ttl);
    Color.set(key, value, { ttl: ttl });

    Color.get(key);

    Color.expire(key, ttl);
2016-08-08 14:48:04 +02:00
Amir-61 8a07627c81 Merge pull request #1018 from strongloop/fix_test_type_2.x
Fix test typo[2.x]
2016-07-24 09:10:36 -04:00
Amir Jafarian 5a594d4855 Fix test case typo
Backport loopback-datasource-juggler#1016
2016-07-24 09:05:33 -04:00
Amir-61 fc2c429963 Merge pull request #1011 from strongloop/remove_unused_vars_2.x
Remove unused variables in model.js [2.x]
2016-07-23 20:11:25 -04:00
Amir Jafarian a96a8f379a Remove unused variables in model.js
Backport loopback-datasource-juggler#1010
2016-07-23 16:56:16 -04:00
Amir-61 fa65094583 Merge pull request #1012 from strongloop/Add_test_for_updateOrCreate_2.x
Add test for updateOrCreate[2.x]
2016-07-23 15:01:50 -04:00
Amir-61 1615caca9d Merge pull request #1013 from strongloop/Declare_definition_2.x
Declare definition[2.x]
2016-07-23 13:19:24 -04:00
Amir Jafarian 6efd00698b Add test for updateOrCreate
* Add test for updateOrCreate when id is not autogenerated Id
Backport loopback-datasource-juggler#989
2016-07-23 11:25:38 -04:00
Amir Jafarian 69cc091ef4 Declare `definition`
* Declare `definition` in `BelongsTo.prototype.destroy`
Backport loopback-datasource-juggler#1009
2016-07-23 11:24:34 -04:00
Supasate Choochaisri f1a70094fa Add test to catch invalid date property
Signed-off-by: Supasate Choochaisri <supasate.c@gmail.com>
See https://github.com/strongloop/loopback-datasource-juggler/pull/1000
2016-07-15 10:47:44 -07:00
Miroslav Bajtoš 4e5ae72eef 2.47.0
* Ensure stable order of items in DAO.find() (Miroslav Bajtoš)
 * give options to validators #984 (RobinBiondi)
 * Update datasource.js (Amir Jafarian)
 * Give warning if PK is changed in hooks (Amir Jafarian)
 * Persist changes on parent for embedsOne (Dimitris Halatsis)
2016-07-14 14:02:12 +02:00
Miroslav Bajtoš 8173a4306c Merge pull request #999 from strongloop/fix/order-in-find-2.x
Ensure stable order of items in DAO.find() [2.x]
2016-07-14 14:01:14 +02:00
Miroslav Bajtoš ebcb5a0546 Ensure stable order of items in DAO.find()
When post-processing result of find operation, use "async.map"
instead of "async.each + array.push" to ensure the order of items
is preserved.
2016-07-13 15:25:34 +02:00
Raymond Feng 6736d72b98 Merge pull request #986 from robinbiondi/postgresConcurency
Fix #984 Give request options(ex: transactions) to validators
2016-06-24 14:42:50 -07:00
RobinBiondi 35f14c38dc give options to validators #984 2016-06-24 22:04:35 +02:00
Amir-61 48e949e996 Merge pull request #980 from strongloop/updata_datasource_2.x
Updata datasource.js
2016-06-17 13:42:22 -04:00
Amir Jafarian 5f8e28a6c0 Update datasource.js
Return a real `Error` message for the default `ping()` method.
2016-06-17 11:36:13 -04:00
Amir-61 892c228797 Merge pull request #968 from strongloop/warning_for_changing_PK_in_hooks_2.x
Give warning if PK is changed in hooks
2016-06-08 18:13:55 -04:00
Amir Jafarian 26718c733a Give warning if PK is changed in hooks
* Give warning if PK is changed in `before save` and `loaded`
 operation hooks for replaceById
2016-06-08 15:54:22 -04:00
Miroslav Bajtoš bfbdd15f1d Merge pull request #949 from mitsos1os/2.x
Persist changes on parent for embedsOne

Close #949
2016-06-07 17:23:14 +02:00
Dimitris Halatsis af8cf19242 Persist changes on parent for embedsOne
Allow direct save of changes on embedded model to be persisted on
parent document.

    Person.embedsOne(Address);
    Person.findById(someId)
      .then(function(p){
        var address = p.addressItem();
        address.street = 'new street'
        // This will now persist changes on parent document
        return address.save();
      })
2016-06-07 17:09:34 +02:00
Raymond Feng baef033115 2.46.1
* fix (Miroslav Bajtoš)
 * Retun err for UPSERT if the connector returns err (Amir Jafarian)
 * Fix error message (Amir Jafarian)
 * Add test's description (Amir Jafarian)
 * ModelBuilder: add new setting strictEmbeddedModels (Dimitris Halatsis)
 * Fix incompatibility between different connectors PR#938 (Amir Jafarian)
 * travis: add v4, v6, drop io.js (Miroslav Bajtoš)
 * Backport ESLint from master (Simon Ho)
 * Set ESLint as devdep (Simon Ho)
 * Use mocha instead of Makefile for testing (Simon Ho)
 * Implement operation hooks for EmbedsMany methods (Miroslav Bajtoš)
 * Implement operation hooks for EmbedsOne methods (Miroslav Bajtoš)
 * eslint config 2.0 + remove extra empty lines (Miroslav Bajtoš)
 * Fix eslint errors in memory connector (Miroslav Bajtoš)
 * Run `eslint --fix` with config from master (Miroslav Bajtoš)
 * Test coverages for hashed password (Amir Jafarian)
 * Fix `forceId` check for `replaceByIds` PR#896 (Amir Jafarian)
 * test: extract hook-monitor helper (Miroslav Bajtoš)
 * test: extract uid-generator helper (Miroslav Bajtoš)
 * test: extract context-test-helpers (Miroslav Bajtoš)
 * Define `patch` aliases (Amir Jafarian)
2016-06-03 14:53:00 -07:00
Amir-61 be17bedcc5 Merge pull request #962 from strongloop/fix2x
fix
2016-06-03 17:46:10 -04:00
Miroslav Bajtoš 9bf60e0d74 fix 2016-06-03 16:29:39 -04:00
Amir-61 534a0e5892 Merge pull request #957 from strongloop/upsert_err
Retun err for UPSERT if the connector returns err
2016-06-02 08:37:02 -04:00
Amir Jafarian dddf0ee09a Retun err for UPSERT if the connector returns err 2016-06-02 08:31:13 -04:00
Amir-61 7aa9cb357d Merge pull request #959 from strongloop/fix_err_msg
Fix error message
2016-06-01 21:00:26 -04:00
Amir-61 2e11dbfe35 Merge pull request #958 from strongloop/Add_test_desc
Add test's description
2016-06-01 20:29:46 -04:00
Amir Jafarian c093648525 Fix error message
* Fix error message when PK is changed in `replaceById`
2016-06-01 16:26:41 -04:00
Amir Jafarian 05ea5467cd Add test's description 2016-06-01 16:05:56 -04:00
Dimitris Halatsis 39907c6421 ModelBuilder: add new setting strictEmbeddedModels
The setting controls the strict mode used for embedded property types,
for example the type of "address" property in this model definition:

    modelBuilder.define('TestEmbedded', {
      name: 'string',
      address: {
        street: 'string',
      },
    });

[back-port of #955]
2016-06-01 13:42:55 +02:00
Amir Jafarian 0d5cff5a0a Fix incompatibility between different connectors PR#938
* Fix incompatibility for replace methods between different connectors.
2016-05-28 09:12:30 -04:00
Miroslav Bajtoš a9f69bcbc6 travis: add v4, v6, drop io.js
[back-port of #943]
2016-05-23 11:24:28 +02:00
Miroslav Bajtoš a6f499703e Merge pull request #928 from strongloop/backport/eslint
Backport eslint from master
2016-05-23 11:03:32 +02:00
Simon Ho 65d6d6a508 Backport ESLint from master 2016-05-20 17:35:23 +02:00
Simon Ho ee6c0ad461 Set ESLint as devdep 2016-05-10 13:37:44 -07:00
Simon Ho 4c7d862cee Use mocha instead of Makefile for testing 2016-05-10 12:06:34 -07:00
Miroslav Bajtoš bec9142100 Implement operation hooks for EmbedsMany methods
create() triggers
 - before save
 - after save

updateById() triggers
 - before save
 - after save

destroy() triggers
 - before delete
 - after delete

The implementation here is intentionally left with less features
than the regular DAO methods provide, the goal is to get a partial
(but still useful!) version released soon.

Limitations:

 - `before save` & `after save` hooks don't provide `ctx.isNewInstance`
 - async validations are not supported yet
 - `persist` and `loaded` hooks are not triggered at all

 - `before delete` hook does not provide `ctx.where` property and
    it's not possible to change the outcome of `destroy()` using this
    hook. Note that regular DAO does support this.

 - updating embedded instances triggers update of the parent (owning)
   model, which is correct and expected. However, the context provided
   by `before save` and `after save` hooks on the parent model is sort of
   arbitrary and may include wrong/extra data. The same probably applies
   to the scenario when deleting embedded instances triggers update of
   the parent model.

[back-port of #911]
2016-04-28 10:15:14 +02:00
Miroslav Bajtoš f65b542dab Implement operation hooks for EmbedsOne methods
create() triggers
 - before save
 - after save

udpate() triggers
 - before save
 - after save

destroy() triggers
 - before delete
 - after delete

The implementation here is intentionally left with less features
than the regular DAO methods provide, the goal is to get a partial
(but still useful!) version released soon.

[back-port of #904]
2016-04-20 10:30:56 +02:00
Miroslav Bajtoš 289eac7565 eslint config 2.0 + remove extra empty lines
Upgrade eslint-config-loopback to ^2.0.0.

Remove extra empty lines to make `npm run lint` pass again.

[back-port of #909]
2016-04-19 17:06:04 +02:00
Miroslav Bajtoš feca7481e9 Merge pull request #905 from strongloop/eslint-autofix-2x
Eslint autofix for 2.x
2016-04-18 16:41:28 +02:00
Miroslav Bajtoš 4000b70e38 Fix eslint errors in memory connector 2016-04-18 16:00:04 +02:00
Miroslav Bajtoš a5187765f6 Run `eslint --fix` with config from master 2016-04-18 16:00:04 +02:00
Amir Jafarian fd1afdf6e6 Test coverages for hashed password
* Test coverages for hashed password for replaceAttributes
* Test coverages for hashed password for updateAttribute
2016-04-15 12:58:36 -04:00
Amir Jafarian 1eeacd0255 Fix `forceId` check for `replaceByIds` PR#896 2016-04-14 17:59:30 -04:00
Miroslav Bajtoš 0de6dd72a4 Merge pull request #902 from strongloop/refactor/context-test-helpers
test: extract helpers used in tests for operation hooks
2016-04-13 15:16:26 +02:00
Miroslav Bajtoš 7d7662bfef test: extract hook-monitor helper 2016-04-13 15:15:55 +02:00
Miroslav Bajtoš 90f04181a4 test: extract uid-generator helper 2016-04-13 15:15:55 +02:00
Miroslav Bajtoš 09ad9e4711 test: extract context-test-helpers 2016-04-13 15:15:53 +02:00
Amir Jafarian 7773963b62 Define `patch` aliases
*Define `patchOrCreate` as an alias for `updateOrCreate`
*Define `PatchAttributes` as an alias for `updateAttributes`
2016-04-12 16:32:17 -04:00
Miroslav Bajtoš 7eee0afb52 2.46.0
* Insert copyright headers (Miroslav Bajtoš)
 * Relicense as MIT only (Miroslav Bajtoš)
 * Fix Mongo compatibility issue (Amir Jafarian)
 * Add automigrate to setup tables for replace test cases (Amir Jafarian)
 * Remove next tag (2.x) (Amir Jafarian)
 * Allow test folder to be published (Amir Jafarian)
 * Fix tests for mysql (Amir Jafarian)
 * Add forgotten unit test (Miroslav Bajtoš)
 * Improve error message on connector init error (Miroslav Bajtoš)
 * Update describe-operation-hooks (Miroslav Bajtoš)
 * Fix missing connector error msg for db2, cloudant for 2.x (Candy)
 * Implementtaion of replace (Amir Jafarian)
2016-04-07 09:12:48 +02:00
Miroslav Bajtoš fbd8b67766 Merge pull request #891 from strongloop/copyright-2.x
Relicense as MIT only
2016-04-07 09:11:04 +02:00
Miroslav Bajtoš 093a69328c Insert copyright headers 2016-04-06 16:51:49 +02:00
Miroslav Bajtoš 216a537a23 Relicense as MIT only 2016-04-06 16:50:02 +02:00
Amir Jafarian 81abac19f3 Fix Mongo compatibility issue
*Fix Mongo compatibility issue for replaceOrCreate
2016-03-30 11:26:35 -04:00
Amir Jafarian 0aa3a2f407 Add automigrate to setup tables for replace test cases 2016-03-29 13:45:17 -04:00
Amir-61 000b5b002a Merge pull request #882 from strongloop/remove_next_tag_2.x
Remove next tag (2.x)
2016-03-29 11:04:56 -04:00
Amir Jafarian de9e0e81b2 Remove next tag (2.x) 2016-03-29 10:58:11 -04:00
Amir Jafarian a73521f161 Allow test folder to be published 2016-03-28 23:32:47 -04:00
Amir Jafarian da9b77a015 Fix tests for mysql 2016-03-28 16:46:11 -04:00
Miroslav Bajtoš 96a9759d6d Add forgotten unit test
The test should have been added as part of #859

[Back-port pull request #862]
2016-03-02 13:15:44 +01:00
Miroslav Bajtoš 0d11186850 Improve error message on connector init error
[Back-port pull request #859]
2016-03-02 12:20:14 +01:00
Miroslav Bajtoš 08faf9f5de Update describe-operation-hooks
- drop custom findOrCreate implementation - memory connector provides
   an atomic implementation out of the box now
 - add new methods `replaceOrCreate` and `replaceByID`
 - fix error reporting to include the stack trace in the console output

[back-port of pull request #847]
2016-02-22 10:46:10 +01:00
Miroslav Bajtoš 0008a1dd6d Merge branch '2.45.x' into 2.x 2016-02-22 10:43:39 +01:00
Miroslav Bajtoš 1268e0f9ac 2.45.2
* Fix missing connector error msg for db2, cloudant for 2.x (Candy)
2016-02-22 10:43:10 +01:00
Candy 1cbeb10c96 Fix missing connector error msg for db2, cloudant for 2.x 2016-02-22 10:42:28 +01:00
Miroslav Bajtoš 27526d90ed Merge branch '2.45.x' into 2.x 2016-02-15 10:23:25 +01:00
Miroslav Bajtoš be9853686d 2.45.1
* Fix conversion for `updateAttributes` (Amir Jafarian)
2016-02-15 10:22:51 +01:00
Candy 79990e31b0 Merge pull request #853 from strongloop/add_new_connectors_2x
Fix missing connector error msg for db2, cloudant for 2.x
2016-02-10 11:10:33 -05:00
Candy 3ed05426be Fix missing connector error msg for db2, cloudant for 2.x 2016-02-10 10:41:46 -05:00
Amir Jafarian ca766f1437 Implementtaion of replace
This includes:
*implementation of replaceAttributes
*implementtaion of replaceOrCreate
2016-02-04 16:05:11 -05:00
Amir Jafarian dbdf915334 Fix conversion for `updateAttributes` 2016-02-03 12:44:58 -05:00
Miroslav Bajtoš 96588622b8 2.45.0
* Refactor `updateAttributes` (Amir Jafarian)
 * Enhance "persisted" hook in DAO.updateAttributes (Miroslav Bajtoš)
 * Add unit test to verify fix for #754 (Tom Kirkpatrick)
 * Implement `findOrCreate` for memory connector (Amir Jafarian)
 * Fix a bug when validation is off for findOrCreate (Amir Jafarian)
 * Fix broken code fencings in the docs (Farid Nouri Neshat)
 * Revert "Correct syntax for should and more" (Simon Ho)
 * Fix test for shouldjs 8.0.2 upgrade (Simon Ho)
 * Upgrade shouldjs to 8.0.2 (Simon Ho)
 * Correct syntax for should and more (Amir Jafarian)
2016-02-02 19:09:48 +01:00
Amir Jafarian 8ca67c3d48 Refactor `updateAttributes` 2016-01-25 11:15:18 -05:00
Miroslav Bajtoš 5fdd521985 Enhance "persisted" hook in DAO.updateAttributes
Add `isNewInstance:false` to the context reported by
DAO.updateAttributes()
2016-01-25 11:13:34 -05:00
Tom Kirkpatrick 2f71622e11 Add unit test to verify fix for #754 2016-01-22 10:13:05 -08:00
Amir Jafarian 3bc2dacb1b Implement `findOrCreate` for memory connector 2016-01-20 11:51:47 -08:00
Amir Jafarian 2423db1571 Fix a bug when validation is off for findOrCreate 2016-01-20 11:51:47 -08:00
Farid Nouri Neshat 174237e6a6 Fix broken code fencings in the docs 2016-01-20 11:51:47 -08:00
Simon Ho 9b1766f1d5 Revert "Correct syntax for should and more"
This reverts commit 62de2ed69e.
2016-01-20 11:51:47 -08:00
Simon Ho e4719638c7 Fix test for shouldjs 8.0.2 upgrade 2016-01-20 11:51:47 -08:00
Simon Ho 95050e3a95 Upgrade shouldjs to 8.0.2 2016-01-20 11:51:47 -08:00
119 changed files with 13526 additions and 5863 deletions

2
.eslintignore Normal file
View File

@ -0,0 +1,2 @@
coverage
support

14
.eslintrc Normal file
View File

@ -0,0 +1,14 @@
{
"extends": "loopback",
"rules": {
"max-len": ["error", 110, 4, {
"ignoreComments": true,
"ignoreUrls": true,
"ignorePattern": "^\\s*var\\s.+=\\s*(require\\s*\\()|(/)"
}],
// NOTE we should eventually remove this override
// and fix all of those 100+ violations
"one-var": "off",
"no-unused-expressions": "off"
}
}

1
.gitignore vendored
View File

@ -12,3 +12,4 @@ docs/man
npm-debug.log
.project
test/memory.json

View File

@ -9,3 +9,4 @@ benchmark.js
analyse.r
docs/html
npm-debug.log
.travis.yml

View File

@ -1,6 +1,6 @@
sudo: false
language: node_js
node_js:
- "0.10"
- "0.12"
- "iojs"
- "6"
- "8"
- "10"

View File

@ -1,3 +1,333 @@
2018-05-18, Version 2.57.0
==========================
* Add support for Node.js 8.x and 10.x, drop 4.x (Miroslav Bajtoš)
2018-01-19, Version 2.56.0
==========================
* fix unauthorized fk change (#1540) (Taranveer Virk)
* drop support for node 0.10 / 0.12 (#1541) (Taranveer Virk)
2017-09-26, Version 2.55.3
==========================
* fix(model-builder): __data may be null. (#1415) (Samuel Reed)
* package: use qs@6.5.0 (#1470) (Kevin Delisle)
2017-07-28, Version 2.55.2
==========================
* Fix #1434 2.55.1 Throws when using where query against "JSON Object or ANY" Type (#1444) (Shing)
* castPropertyValue: throw on malformed types (Kevin Delisle)
2017-07-21, Version 2.55.1
==========================
* Catch errors using cb (loay)
* Recursively cast props on fromDb for memory conn. (nVitius)
* Recursively coerce nested properties in dao (nVitius)
* Fix #1079 - Polymorphic hasMany inverse relation (#1296) (Janny)
2017-07-10, Version 2.55.0
==========================
* Backport - Apply hasManyThrough filter on target model (#1404) (Janny)
2017-06-13, Version 2.54.2
==========================
* Fix assert as array does not guarantee order (Candy)
* Fixed empty objList in linkOneToMany fn (#1287) (somename85)
* remove equality value for user-defined id (#1292) (Matteo Padovano)
* override collection name for arangodb (#1274) (Matteo Padovano)
* fix missing findOrCreate error callback (Ryan Graham)
2017-03-13, Version 2.54.1
==========================
* fix incorrect engines property in package.json (Ryan Graham)
* Fix datasource to report connector-loading errors (Miroslav Bajtoš)
* Add two basic tests for "inq" operator (Miroslav Bajtoš)
2017-01-17, Version 2.54.0
==========================
* Replace deprecated node-uuid with uuid module (Raymond Feng)
* Coerce array-like objects into arrays (Heath Morrison)
* Throw error when model relation name is trigger (Brian Schemp)
* Update package.json for LB3 release (Simon Ho)
2016-12-21, Version 2.53.1
==========================
* Fix HasOne.update to propagate options arg (Miroslav Bajtoš)
* Update ko translation file (Candy)
* Back-port fixes for linter errors from master (Miroslav Bajtoš)
* Update chinese simplified translation file (Candy)
* Apply style guide to test names (Amir Jafarian)
* Continue _coerce after logical operators (Heath Morrison)
* Fix manually (Amir Jafarian)
* Auto-update by eslint --fix (Amir Jafarian)
* update eslintrc according to master branch (Amir Jafarian)
* Update eslint (Amir Jafarian)
* test/kvao: add connectorCapabilities options (Miroslav Bajtoš)
* Update validations.js (Rand McKinney)
* Fix bug when near filter is used (Amir Jafarian)
2016-10-13, Version 2.52.0
==========================
* Support {defaultFn: 'shortid'} (#1110) (Simon Ho)
* Add ilike and nilike operators (#1136) (Simon Ho)
* Fix JSdoc (Amir Jafarian)
* Update ja translation file (Candy)
* Update translation files - round#2 (Candy)
* Add 'isNewInstance' for updateAttributes (Amir Jafarian)
* Add globalization strings (Amir Jafarian)
* Add docs for KVAO (Simon Ho)
* Skip test temporarily (Loay)
2016-09-12, Version 2.51.0
==========================
* Add missing "done" arg in test/kvao/ttl.suite (Miroslav Bajtoš)
* Support nested queries for arrays (pponugo)
* Remove expired item before executing expire (Simon Ho)
* Fix failures for connectors (Amir Jafarian)
* upsertWithWhere feature support in juggler DAO (Sonali Samantaray)
* Remove ESLint from dependencies (Simon Ho)
* Refactor TTL test suite (Simon Ho)
2016-08-26, Version 2.50.0
==========================
* test/memory: remove dummy findOrCreate impl (Miroslav Bajtoš)
* Implement `notify` for find method (Amir Jafarian)
* kvao: implement key filter (Miroslav Bajtoš)
* kvao: add iterateKeys() and keys() (Miroslav Bajtoš)
* Globalize KeyValue Memory connector (Simon Ho)
* test: Rename KVAO get test suite (Simon Ho)
2016-08-16, Version 2.49.0
==========================
* Fix error message for missing global.Promise (Miroslav Bajtoš)
* Add TTL for KeyValue related features (Simon Ho)
* test/kvao: setup global.Promise on Node v0.10 (Miroslav Bajtoš)
* kv-memory: fix crash in regular cleanup (Miroslav Bajtoš)
* Backport globalization (Amir Jafarian)
2016-08-09, Version 2.48.0
==========================
* Return error if connector does not implement (Amir Jafarian)
* kvao: return 404 when expiring unknown key (Miroslav Bajtoš)
* Update doc for `validatesFormatOf` (Amir Jafarian)
* Implement KeyValue API and memory connector (Miroslav Bajtoš)
* Fix test case typo (Amir Jafarian)
* Remove unused variables in model.js (Amir Jafarian)
* Add test for updateOrCreate (Amir Jafarian)
* Declare `definition` (Amir Jafarian)
* Add test to catch invalid date property (Supasate Choochaisri)
2016-07-14, Version 2.47.0
==========================
* Ensure stable order of items in DAO.find() (Miroslav Bajtoš)
* give options to validators #984 (RobinBiondi)
* Update datasource.js (Amir Jafarian)
* Give warning if PK is changed in hooks (Amir Jafarian)
* Persist changes on parent for embedsOne (Dimitris Halatsis)
2016-06-03, Version 2.46.1
==========================
* fix (Miroslav Bajtoš)
* Retun err for UPSERT if the connector returns err (Amir Jafarian)
* Fix error message (Amir Jafarian)
* Add test's description (Amir Jafarian)
* ModelBuilder: add new setting strictEmbeddedModels (Dimitris Halatsis)
* Fix incompatibility between different connectors PR#938 (Amir Jafarian)
* travis: add v4, v6, drop io.js (Miroslav Bajtoš)
* Backport ESLint from master (Simon Ho)
* Set ESLint as devdep (Simon Ho)
* Use mocha instead of Makefile for testing (Simon Ho)
* Implement operation hooks for EmbedsMany methods (Miroslav Bajtoš)
* Implement operation hooks for EmbedsOne methods (Miroslav Bajtoš)
* eslint config 2.0 + remove extra empty lines (Miroslav Bajtoš)
* Fix eslint errors in memory connector (Miroslav Bajtoš)
* Run `eslint --fix` with config from master (Miroslav Bajtoš)
* Test coverages for hashed password (Amir Jafarian)
* Fix `forceId` check for `replaceByIds` PR#896 (Amir Jafarian)
* test: extract hook-monitor helper (Miroslav Bajtoš)
* test: extract uid-generator helper (Miroslav Bajtoš)
* test: extract context-test-helpers (Miroslav Bajtoš)
* Define `patch` aliases (Amir Jafarian)
2016-04-07, Version 2.46.0
==========================
* Insert copyright headers (Miroslav Bajtoš)
* Relicense as MIT only (Miroslav Bajtoš)
* Fix Mongo compatibility issue (Amir Jafarian)
* Add automigrate to setup tables for replace test cases (Amir Jafarian)
* Remove next tag (2.x) (Amir Jafarian)
* Allow test folder to be published (Amir Jafarian)
* Fix tests for mysql (Amir Jafarian)
* Add forgotten unit test (Miroslav Bajtoš)
* Improve error message on connector init error (Miroslav Bajtoš)
* Update describe-operation-hooks (Miroslav Bajtoš)
* Fix missing connector error msg for db2, cloudant for 2.x (Candy)
* Implementtaion of replace (Amir Jafarian)
2016-02-22, Version 2.45.2
==========================
* Fix missing connector error msg for db2, cloudant for 2.x (Candy)
2016-02-15, Version 2.45.1
==========================
* Fix conversion for `updateAttributes` (Amir Jafarian)
2016-02-02, Version 2.45.0
==========================
* Refactor `updateAttributes` (Amir Jafarian)
* Enhance "persisted" hook in DAO.updateAttributes (Miroslav Bajtoš)
* Add unit test to verify fix for #754 (Tom Kirkpatrick)
* Implement `findOrCreate` for memory connector (Amir Jafarian)
* Fix a bug when validation is off for findOrCreate (Amir Jafarian)
* Fix broken code fencings in the docs (Farid Nouri Neshat)
* Revert "Correct syntax for should and more" (Simon Ho)
* Fix test for shouldjs 8.0.2 upgrade (Simon Ho)
* Upgrade shouldjs to 8.0.2 (Simon Ho)
* Correct syntax for should and more (Amir Jafarian)
2015-12-13, Version 2.44.0
==========================
@ -1455,8 +1785,6 @@
2014-06-24, Version 2.0.0-beta2
===============================
* 2.0.0-beta2 (Miroslav Bajtoš)
* validations: support non-V8 browsers (Miroslav Bajtoš)
* Work around for Date default (Raymond Feng)
@ -2074,12 +2402,12 @@
* Update LDL doc for the strict mode (Raymond Feng)
2013-09-12, Version strongloopsuite-1.0.0-4
2013-09-12, Version strongloopsuite-1.0.0-5
===========================================
2013-09-12, Version strongloopsuite-1.0.0-5
2013-09-12, Version strongloopsuite-1.0.0-4
===========================================
* Allow connector to report failure during initialization (Raymond Feng)

25
LICENSE Normal file
View File

@ -0,0 +1,25 @@
Copyright (c) IBM Corp. 2011,2016. All Rights Reserved.
Node module: loopback-datasource-juggler
This project is licensed under the MIT License, full text below.
--------
MIT license
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

View File

@ -1,9 +0,0 @@
Copyright (c) 2013-2015 StrongLoop, Inc.
loopback-datasource-juggler uses a dual license model.
You may use this library under the terms of the [MIT License][],
or under the terms of the [StrongLoop Subscription Agreement][].
[MIT License]: http://opensource.org/licenses/MIT
[StrongLoop Subscription Agreement]: http://strongloop.com/license

View File

@ -1,43 +0,0 @@
TESTER = ./node_modules/.bin/mocha
OPTS = --growl
TESTS = test/*.test.js
default: help
.PHONY: clean
clean:
rm -rf $(CURDIR)/node_modules
.PHONY: help
help:
@echo 'Usage: make [target]'
@echo 'Targets:'
@echo ' clean Delete `node_modules`'
@echo ' help Print help (this message)'
@echo ' refresh Delete `node_modules` and run `npm install`'
@echo ' test Run tests in silent mode'
@echo ' test-verbose Run tests in verbose mode'
@echo ' testing Run tests continuously'
.PHONY: refresh
refresh: clean
npm install
.PHONY: test
test:
NO_DEPRECATION=loopback-datasource-juggler $(TESTER) $(OPTS) $(TESTS)
.PHONY: test-verbose
test-verbose:
$(TESTER) $(OPTS) --reporter spec $(TESTS)
.PHONY: testing
testing:
$(TESTER) $(OPTS) --watch $(TESTS)
# Deprecated targets
.PHONY: about-testing
about-testing:
@echo 'DEPRECATED: Use `make help` instead'
make help

View File

@ -18,5 +18,3 @@ Also install the appropriated connector, for example for mongodb:
npm install loopback-connector-mongodb
See [StrongLoop Documentation](http://docs.strongloop.com/) for more information.

View File

@ -1,3 +1,9 @@
// Copyright IBM Corp. 2013,2016. All Rights Reserved.
// Node module: loopback-datasource-juggler
// This file is licensed under the MIT License.
// License text available at https://opensource.org/licenses/MIT
'use strict';
var DataSource = require('../../loopback-datasource-juggler').DataSource;
var ModelBuilder = require('../../loopback-datasource-juggler').ModelBuilder;
var introspectType = require('../lib/introspection')(ModelBuilder);
@ -12,29 +18,29 @@ var application = {
name: 'MyApp1',
description: 'My first app',
pushSettings: [
{ "platform": "apns",
"apns": {
"pushOptions": {
"gateway": "gateway.sandbox.push.apple.com",
"cert": "credentials/apns_cert_dev.pem",
"key": "credentials/apns_key_dev.pem"
{'platform': 'apns',
'apns': {
'pushOptions': {
'gateway': 'gateway.sandbox.push.apple.com',
'cert': 'credentials/apns_cert_dev.pem',
'key': 'credentials/apns_key_dev.pem',
},
"feedbackOptions": {
"gateway": "feedback.sandbox.push.apple.com",
"cert": "credentials/apns_cert_dev.pem",
"key": "credentials/apns_key_dev.pem",
"batchFeedback": true,
"interval": 300
}
}}
]}
'feedbackOptions': {
'gateway': 'feedback.sandbox.push.apple.com',
'cert': 'credentials/apns_cert_dev.pem',
'key': 'credentials/apns_key_dev.pem',
'batchFeedback': true,
'interval': 300,
},
}},
]};
console.log(new Application(application).toObject());
Application.create(application, function (err, app1) {
Application.create(application, function(err, app1) {
console.log('Created: ', app1.toObject());
Application.findById(app1.id, function (err, app2) {
Application.findById(app1.id, function(err, app2) {
console.log('Found: ', app2.toObject());
});
});
@ -50,14 +56,14 @@ var user = {
city: 'San Jose',
state: 'CA',
zipcode: '95131',
country: 'US'
country: 'US',
},
friends: ['John', 'Mary'],
emails: [
{label: 'work', id: 'x@sample.com'},
{label: 'home', id: 'x@home.com'}
{label: 'home', id: 'x@home.com'},
],
tags: []
tags: [],
};
// Introspect the JSON document to generate a schema
@ -71,11 +77,9 @@ var obj = new User(user);
console.log(obj.toObject());
User.create(user, function (err, u1) {
User.create(user, function(err, u1) {
console.log('Created: ', u1.toObject());
User.findById(u1.id, function (err, u2) {
User.findById(u1.id, function(err, u2) {
console.log('Found: ', u2.toObject());
});
});

View File

@ -1,14 +1,20 @@
// Copyright IBM Corp. 2013,2016. All Rights Reserved.
// Node module: loopback-datasource-juggler
// This file is licensed under the MIT License.
// License text available at https://opensource.org/licenses/MIT
'use strict';
var ModelBuilder = require('../../loopback-datasource-juggler').ModelBuilder;
var modelBuilder = new ModelBuilder();
// define models
var Post = modelBuilder.define('Post', {
title: { type: String, length: 255 },
content: { type: ModelBuilder.Text },
date: { type: Date, default: function () {
title: {type: String, length: 255},
content: {type: ModelBuilder.Text},
date: {type: Date, default: function() {
return new Date();
} },
timestamp: { type: Number, default: Date.now },
published: { type: Boolean, default: false, index: true }
}},
timestamp: {type: Number, default: Date.now},
published: {type: Boolean, default: false, index: true},
});
// simpler way to describe model
@ -17,13 +23,13 @@ var User = modelBuilder.define('User', {
bio: ModelBuilder.Text,
approved: Boolean,
joinedAt: Date,
age: Number
age: Number,
});
var Group = modelBuilder.define('Group', {group: String});
// define any custom method
User.prototype.getNameAndAge = function () {
User.prototype.getNameAndAge = function() {
return this.name + ', ' + this.age;
};
@ -36,7 +42,3 @@ console.log(modelBuilder.definitions);
User.mixin(Group);
var user = new User({name: 'Ray', group: 'Admin'});
console.log(user);

View File

@ -1,16 +1,22 @@
// Copyright IBM Corp. 2013,2016. All Rights Reserved.
// Node module: loopback-datasource-juggler
// This file is licensed under the MIT License.
// License text available at https://opensource.org/licenses/MIT
'use strict';
var DataSource = require('../../loopback-datasource-juggler').DataSource;
var ModelBuilder = require('../../loopback-datasource-juggler').ModelBuilder;
var ds = new DataSource('memory');
// define models
var Post = ds.define('Post', {
title: { type: String, length: 255 },
content: { type: DataSource.Text },
date: { type: Date, default: function () {
title: {type: String, length: 255},
content: {type: DataSource.Text},
date: {type: Date, default: function() {
return new Date;
} },
timestamp: { type: Number, default: Date.now },
published: { type: Boolean, default: false, index: true }
}},
timestamp: {type: Number, default: Date.now},
published: {type: Boolean, default: false, index: true},
});
// simplier way to describe model
@ -19,13 +25,13 @@ var User = ds.define('User', {
bio: DataSource.Text,
approved: Boolean,
joinedAt: Date,
age: Number
age: Number,
});
var Group = ds.define('Group', {name: String});
// define any custom method
User.prototype.getNameAndAge = function () {
User.prototype.getNameAndAge = function() {
return this.name + ', ' + this.age;
};
@ -52,19 +58,19 @@ Post.belongsTo(User, {as: 'author', foreignKey: 'userId'});
User.hasAndBelongsToMany('groups');
var user2 = new User({name: 'Smith', age: 14});
user2.save(function (err) {
user2.save(function(err) {
console.log(user2);
var post = user2.posts.build({title: 'Hello world'});
post.save(function (err, data) {
post.save(function(err, data) {
console.log(err ? err : data);
});
});
Post.findOne({where: {published: false}, order: 'date DESC'}, function (err, data) {
Post.findOne({where: {published: false}, order: 'date DESC'}, function(err, data) {
console.log(data);
});
User.create({name: 'Jeff', age: 12}, function (err, data) {
User.create({name: 'Jeff', age: 12}, function(err, data) {
if (err) {
console.log(err);
return;
@ -74,12 +80,12 @@ User.create({name: 'Jeff', age: 12}, function (err, data) {
console.log(post);
});
User.create({name: 'Ray'}, function (err, data) {
User.create({name: 'Ray'}, function(err, data) {
console.log(data);
});
User.scope('minors', {where: {age: {lte: 16}}, include: 'posts'});
User.minors(function (err, kids) {
User.minors(function(err, kids) {
console.log('Kids: ', kids);
});
@ -87,10 +93,10 @@ var Article = ds.define('Article', {title: String});
var Tag = ds.define('Tag', {name: String});
Article.hasAndBelongsToMany('tags');
Article.create(function (e, article) {
article.tags.create({name: 'popular'}, function (err, data) {
Article.findOne(function (e, article) {
article.tags(function (e, tags) {
Article.create(function(e, article) {
article.tags.create({name: 'popular'}, function(err, data) {
Article.findOne(function(e, article) {
article.tags(function(e, tags) {
console.log(tags);
});
});
@ -100,8 +106,8 @@ Article.create(function (e, article) {
// should be able to attach a data source to an existing model
var modelBuilder = new ModelBuilder();
Color = modelBuilder.define('Color', {
name: String
var Color = modelBuilder.define('Color', {
name: String,
});
// attach
@ -111,7 +117,7 @@ Color.create({name: 'red'});
Color.create({name: 'green'});
Color.create({name: 'blue'});
Color.all(function (err, colors) {
Color.all(function(err, colors) {
console.log(colors);
});

View File

@ -1,32 +1,36 @@
// Copyright IBM Corp. 2014,2016. All Rights Reserved.
// Node module: loopback-datasource-juggler
// This file is licensed under the MIT License.
// License text available at https://opensource.org/licenses/MIT
'use strict';
var jdb = require('../index');
var User, Post, Passport, City, Street, Building;
var nbSchemaRequests = 0;
setup(function () {
Passport.find({include: 'owner'}, function (err, passports) {
setup(function() {
Passport.find({include: 'owner'}, function(err, passports) {
console.log('passports.owner', passports);
});
User.find({include: 'posts'}, function (err, users) {
User.find({include: 'posts'}, function(err, users) {
console.log('users.posts', users);
});
Passport.find({include: {owner: 'posts'}}, function (err, passports) {
Passport.find({include: {owner: 'posts'}}, function(err, passports) {
console.log('passports.owner.posts', passports);
});
Passport.find({
include: {owner: {posts: 'author'}}
}, function (err, passports) {
include: {owner: {posts: 'author'}},
}, function(err, passports) {
console.log('passports.owner.posts.author', passports);
});
User.find({include: ['posts', 'passports']}, function (err, users) {
User.find({include: ['posts', 'passports']}, function(err, users) {
console.log('users.passports && users.posts', users);
});
});
function setup(done) {
@ -36,13 +40,13 @@ function setup(done) {
Building = db.define('Building');
User = db.define('User', {
name: String,
age: Number
age: Number,
});
Passport = db.define('Passport', {
number: String
number: String,
});
Post = db.define('Post', {
title: String
title: String,
});
Passport.belongsTo('owner', {model: User});
@ -50,7 +54,7 @@ function setup(done) {
User.hasMany('posts', {foreignKey: 'userId'});
Post.belongsTo('author', {model: User, foreignKey: 'userId'});
db.automigrate(function () {
db.automigrate(function() {
var createdUsers = [];
var createdPassports = [];
var createdPosts = [];
@ -63,9 +67,9 @@ function setup(done) {
{name: 'User B', age: 22},
{name: 'User C', age: 23},
{name: 'User D', age: 24},
{name: 'User E', age: 25}
{name: 'User E', age: 25},
],
function (items) {
function(items) {
createdUsers = items;
createPassports();
}
@ -78,9 +82,9 @@ function setup(done) {
[
{number: '1', ownerId: createdUsers[0].id},
{number: '2', ownerId: createdUsers[1].id},
{number: '3'}
{number: '3'},
],
function (items) {
function(items) {
createdPassports = items;
createPosts();
}
@ -95,21 +99,20 @@ function setup(done) {
{title: 'Post B', userId: createdUsers[0].id},
{title: 'Post C', userId: createdUsers[0].id},
{title: 'Post D', userId: createdUsers[1].id},
{title: 'Post E'}
{title: 'Post E'},
],
function (items) {
function(items) {
createdPosts = items;
done();
}
);
}
});
}
function clearAndCreate(model, data, callback) {
var createdItems = [];
model.destroyAll(function () {
model.destroyAll(function() {
nextItem(null, null);
});

View File

@ -1,3 +1,9 @@
// Copyright IBM Corp. 2013,2016. All Rights Reserved.
// Node module: loopback-datasource-juggler
// This file is licensed under the MIT License.
// License text available at https://opensource.org/licenses/MIT
'use strict';
var path = require('path'),
fs = require('fs'),
DataSource = require('../lib/datasource').DataSource;
@ -17,7 +23,6 @@ function loadSchemasSync(schemaFile, dataSource) {
var schemas = JSON.parse(fs.readFileSync(schemaFile));
return dataSource.buildModels(schemas);
}
var models = loadSchemasSync(path.join(__dirname, 'jdb-schemas.json'));

View File

@ -1,3 +1,9 @@
// Copyright IBM Corp. 2013,2016. All Rights Reserved.
// Node module: loopback-datasource-juggler
// This file is licensed under the MIT License.
// License text available at https://opensource.org/licenses/MIT
'use strict';
var ModelBuilder = require('../../loopback-datasource-juggler').ModelBuilder;
var modelBuilder = new ModelBuilder();
@ -13,20 +19,27 @@ var User = modelBuilder.define('User', {
city: String,
state: String,
zipCode: String,
country: String
country: String,
},
emails: [
{
label: String,
email: String
}
email: String,
},
],
friends: [String]
friends: [String],
});
var user = new User({name: 'Joe', age: 20, address: {street: '123 Main St', 'city': 'San Jose', state: 'CA'},
var user = new User({
name: 'Joe',
age: 20,
address: {
street: '123 Main St',
city: 'San Jose',
state: 'CA',
},
emails: [
{label: 'work', email: 'xyz@sample.com'}
{label: 'work', email: 'xyz@sample.com'},
],
friends: ['John', 'Mary']});
console.log(user);

View File

@ -1,34 +1,39 @@
// Copyright IBM Corp. 2013,2016. All Rights Reserved.
// Node module: loopback-datasource-juggler
// This file is licensed under the MIT License.
// License text available at https://opensource.org/licenses/MIT
'use strict';
var DataSource = require('../index').DataSource;
var ds = new DataSource('memory');
var Order = ds.createModel('Order', {
items: [String],
orderDate: Date,
qty: Number
qty: Number,
});
var Customer = ds.createModel('Customer', {
name: String
name: String,
});
Order.belongsTo(Customer);
var order1, order2, order3;
Customer.create({name: 'John'}, function (err, customer) {
Order.create({customerId: customer.id, orderDate: new Date(), items: ['Book']}, function (err, order) {
Customer.create({name: 'John'}, function(err, customer) {
Order.create({customerId: customer.id, orderDate: new Date(), items: ['Book']}, function(err, order) {
order1 = order;
order.customer(console.log);
order.customer(true, console.log);
Customer.create({name: 'Mary'}, function (err, customer2) {
Customer.create({name: 'Mary'}, function(err, customer2) {
order.customer(customer2);
order.customer(console.log);
});
});
Order.create({orderDate: new Date(), items: ['Phone']}, function (err, order) {
Order.create({orderDate: new Date(), items: ['Phone']}, function(err, order) {
order.customer.create({name: 'Smith'}, function(err, customer2) {
console.log(order, customer2);
order.save(function(err, order) {
@ -43,13 +48,13 @@ Customer.create({name: 'John'}, function (err, customer) {
Customer.hasMany(Order, {as: 'orders', foreignKey: 'customerId'});
Customer.create({name: 'Ray'}, function (err, customer) {
Order.create({customerId: customer.id, qty: 3, orderDate: new Date()}, function (err, order) {
Customer.create({name: 'Ray'}, function(err, customer) {
Order.create({customerId: customer.id, qty: 3, orderDate: new Date()}, function(err, order) {
order3 = order;
customer.orders(console.log);
customer.orders.create({orderDate: new Date(), qty: 4}, function (err, order) {
customer.orders.create({orderDate: new Date(), qty: 4}, function(err, order) {
console.log(order);
Customer.include([customer], 'orders', function (err, results) {
Customer.include([customer], 'orders', function(err, results) {
console.log('Results: ', results);
});
customer.orders({where: {qty: 4}}, function(err, results) {
@ -62,17 +67,17 @@ Customer.create({name: 'Ray'}, function (err, customer) {
});
var Physician = ds.createModel('Physician', {
name: String
name: String,
});
var Patient = ds.createModel('Patient', {
name: String
name: String,
});
var Appointment = ds.createModel('Appointment', {
physicianId: Number,
patientId: Number,
appointmentDate: Date
appointmentDate: Date,
});
Appointment.belongsTo(Patient);
@ -81,49 +86,64 @@ Appointment.belongsTo(Physician);
Physician.hasMany(Patient, {through: Appointment});
Patient.hasMany(Physician, {through: Appointment});
Physician.create({name: 'Dr John'}, function (err, physician1) {
Physician.create({name: 'Dr Smith'}, function (err, physician2) {
Patient.create({name: 'Mary'}, function (err, patient1) {
Patient.create({name: 'Ben'}, function (err, patient2) {
Appointment.create({appointmentDate: new Date(), physicianId: physician1.id, patientId: patient1.id},
function (err, appt1) {
Appointment.create({appointmentDate: new Date(), physicianId: physician1.id, patientId: patient2.id},
function (err, appt2) {
physician1.patients(console.log);
physician1.patients({where: {name: 'Mary'}}, console.log);
patient1.physicians(console.log);
Physician.create({name: 'Dr John'}, function(err, physician1) {
Physician.create({name: 'Dr Smith'}, function(err, physician2) {
Patient.create({name: 'Mary'}, function(err, patient1) {
Patient.create({name: 'Ben'}, function(err, patient2) {
Appointment.create({
appointmentDate: new Date(),
physicianId: physician1.id,
patientId: patient1.id,
}, function(err, appt1) {
Appointment.create({
appointmentDate: new Date(),
physicianId: physician1.id,
patientId: patient2.id,
}, function(err, appt2) {
physician1.patients(console.log);
physician1.patients({where: {name: 'Mary'}}, console.log);
patient1.physicians(console.log);
// Build an appointment?
var patient3 = patient1.physicians.build({name: 'Dr X'});
console.log('Physician 3: ', patient3, patient3.constructor.modelName);
// Build an appointment?
var patient3 = patient1.physicians.build({name: 'Dr X'});
console.log(
'Physician 3: ',
patient3,
patient3.constructor.modelName
);
// Create a physician?
patient1.physicians.create({name: 'Dr X'}, function(err, patient4) {
console.log('Physician 4: ', patient4, patient4.constructor.modelName);
});
});
// Create a physician?
patient1.physicians.create(
{name: 'Dr X'},
function(err, patient4) {
console.log(
'Physician 4: ',
patient4,
patient4.constructor.modelName
);
}
);
});
});
});
});
});
});
var Assembly = ds.createModel('Assembly', {
name: String
name: String,
});
var Part = ds.createModel('Part', {
partNumber: String
partNumber: String,
});
Assembly.hasAndBelongsToMany(Part);
Part.hasAndBelongsToMany(Assembly);
Assembly.create({name: 'car'}, function (err, assembly) {
Part.create({partNumber: 'engine'}, function (err, part) {
assembly.parts.add(part, function (err) {
Assembly.create({name: 'car'}, function(err, assembly) {
Part.create({partNumber: 'engine'}, function(err, part) {
assembly.parts.add(part, function(err) {
assembly.parts(function(err, parts) {
console.log('Parts: ', parts);
});
@ -141,7 +161,6 @@ Assembly.create({name: 'car'}, function (err, assembly) {
});
});
});
});
});

View File

@ -1,3 +1,12 @@
// Copyright IBM Corp. 2011,2016. All Rights Reserved.
// Node module: loopback-datasource-juggler
// This file is licensed under the MIT License.
// License text available at https://opensource.org/licenses/MIT
'use strict';
var SG = require('strong-globalize');
SG.SetRootDir(__dirname);
exports.ModelBuilder = exports.LDL = require('./lib/model-builder.js').ModelBuilder;
exports.DataSource = exports.Schema = require('./lib/datasource.js').DataSource;
exports.ModelBaseClass = require('./lib/model.js');
@ -5,12 +14,14 @@ exports.GeoPoint = require('./lib/geo.js').GeoPoint;
exports.ValidationError = require('./lib/validations.js').ValidationError;
Object.defineProperty(exports, 'version', {
get: function() {return require('./package.json').version;}
get: function() { return require('./package.json').version; },
});
var commonTest = './test/common_test';
Object.defineProperty(exports, 'test', {
get: function() {return require(commonTest);}
get: function() { return require(commonTest); },
});
exports.Transaction = require('loopback-connector').Transaction;
exports.KeyValueAccessObject = require('./lib/kvao');

74
intl/de/messages.json Normal file
View File

@ -0,0 +1,74 @@
{
"0bd753a8944ad0af85a939bb25273887": "Unbekannter Schlüssel {0} darf nicht ablaufen",
"bec226891a505828bfc76c5cfd73b336": "TTL für unbekannten Schlüssel {0} kann nicht abgerufen werden",
"4c78325cedbb826db3a05bf5df0e8546": "Sie müssen beim Ersetzen eine {{id}} angeben!",
"a0cf0e09c26df14283223e84e6a10f00": "Attribute konnten nicht aktualisiert werden. {{Object}} mit {{id}} {0} ist nicht vorhanden!",
"e54d944c2a2c85a23caa86027ae307cf": "Modelle, die nicht an diese Datenquelle angehängt sind, können nicht migriert werden: {0}",
"e6161ae8459c79d810e2aa9d21282a39": "Sie müssen beim Aktualisieren von Attributen eine {{id}} angeben!",
"fca4d12faff1035d9d0438d73432571b": "Doppelter Eintrag für {0}.{1}",
"09483e03b91c8bd58732a74b3ef1ec13": "Ungültiges Datum: {0}",
"0b16d3ffc42f91b4b9a4b3b50c41c838": "Die Reihenfolge {0} ist nicht gültig",
"0e88a84c6bc2638fbcc5d3ea95181f99": "Unbekannte Eigenschaft: {0}",
"21095484501dbff31af6556fa6039182": "Der {{offset/skip}}-Parameter {0} ist nicht gültig",
"416dfbb7b823f51c9f3800be81060b41": "Keine Instanz mit {{id}} {0} für {1} gefunden",
"4e31b1edd10dadb724d83387de0b5062": "Der {{limit}}-Parameter {0} ist nicht gültig",
"5ec8efeb715a2c34b440f2d76e2cf87d": " {0}",
"62a2d80c405b7fec5f547c448ab1b6ff": "Die {{order}} {0} hat eine ungültige Richtung",
"63f503acabdc4c7588aab741b8466862": "Die Eigenschaft {0} weist die ungültige Klausel {1} auf",
"67c2bf43b5281ab929617423ea8a6f3e": "Der Konnektor {0} unterstützt die Operation {{replaceById}} nicht. Hierbei handelt es sich nicht um einen Fehler in LoopBack. Wenden Sie sich an die Ersteller des Konnektors, vorzugsweise über GitHub-Probleme.",
"6c3234937d69763fc7f6bcafccc59bbc": "{{Model::deleteById}} erfordert das Argument {{id}}",
"6eb6fd4fbd73394000bc25f5776fd20c": "{{Model::exists}} erfordert das Argument {{id}}",
"7bbbdece4eea90e42aa5c0bce295e503": "{{Model::findById}} erfordert das Argument {{id}}",
"9da58ab4555ac50f4999883de6470d7d": "Der Operator {0} weist ungültige Klauseln {1} auf",
"a829dee089c912e68c18920ba015400c": "WARNUNG: Eigenschaft {{id}} darf für Modell {2} in Operationshook {{'loaded'}} nicht von {0} in {1} geändert werden",
"a984a076c59e451948b2bcf7a393d860": "WARNUNG: Eigenschaft {{id}} darf für Modell {2} in Operationshook {{'before save'}} nicht von {0} in {1} geändert werden",
"baf2c8b0c5a574b8a894e9b6304fece1": "Bei der where-Klausel {0} handelt es sich nicht um ein {{object}}",
"db03083e9a768388fdbee865249ac67a": "Validierungsfehler in {{updateOrCreate()}} werden ignoriert:",
"e4434de4bb8f5a3cd1d416e4d80d7e0b": "Unbekannte \"{0}\"-{{id}} \"{1}\".",
"e54f118a75e15e132f16b985274eb46d": "Beim Abfragefilter {0} handelt es sich nicht um ein {{object}}",
"f6e8c96c93b9c7687d6c172b3695e898": "{{id}}-Eigenschaft ({0}) kann nicht von {1} in {2} aktualisiert werden",
"0be2d39d225b1d8b2a0f92ad5c65c9ac": "Kein Modell für {{polymorphic}} {0} angegeben: {1}",
"2f4af31c144bbfab1bbf479866acd820": "\nWARNUNG: {{LoopBack}}-Konnektor \"{0}\" ist als keines der folgenden Module installiert:\n\n {1}\n\nFühren Sie zur Behebung Folgendes aus:\n\n {{npm install {2} --save}}\n",
"6111399276924ffa3bc9a410cdfcb2e5": "Kein {{id}}-Name {0}",
"75518263527c423b8a12f908a2730b0f": "{{className}} ist erforderlich",
"791ab3031a73ede03f7d6299a85e8289": "Zeitlimitüberschreitung bei Verbindungsherstellung nach {0} ms",
"7b277018e43d41bc445731092b91547d": "Nicht verbunden",
"a2487abefef4259c2131d96cdb8543b1": "Verbindungsfehler: {0}\nNach der nächsten Anforderung findet ein Neuversuch statt.",
"b15b20280211ad258d92947f05b6e4a5": "Der Konnektor wurde nicht initialisiert.",
"e0e9504e137a3c3339144b51ed76fef2": "Konnektor ist nicht ordnungsgemäß definiert: Er sollte ein {{connector}}-Member von dataSource erstellen",
"ec42dca074f1818c447f7ad16e2d01af": "{0} wird vom zugeordneten Konnektor nicht angegeben",
"ba0fd8106eb54de4d003a844206431fd": "Modellhook \"{0}\" ist veraltet, verwenden Sie stattdessen Operationshooks. {{http://docs.strongloop.com/display/LB/Operation+hooks}}",
"280f4550f90e133118955ec6f6f72830": "Diskriminatortyp {0} angegeben, aber es ist kein Modell mit diesem Namen vorhanden",
"38dbf42c29a4645238cc3d632e88ebc9": "{{Relation.modelTo}} ist für Beziehung {0} nicht definiert und ist keine {{polymorphic}}",
"eb56c2b0c30cf006e2df00a549ec9c2c": "Beziehung \"{0}\" ist nicht definiert für {1} modell",
"938401ea4ce48159efa9be1d4a5e8bab": "Elemente müssen ein Array sein: {0}",
"f41bd91dc0f000a79c0bf842f1b7fdf9": "konnte Liste nicht aus JSON-Zeichenfolge erstellen: {0}",
"514985b2327f061ffb1c932f6b909979": "Modell {0} ist nicht definiert.",
"cc09e361de7b0f3dc1687515c5713885": "Eigenschaftsnamen, die einen Punkt enthalten, werden nicht unterstützt. Modell: {0}, Eigenschaft: {1}",
"da02dd6c53d4148320eeb31718a7aebe": "Ungültiger Typ für Eigenschaft {0}",
"ddf0aa14803f1c84f4a97f3803f7471c": "Klassenname erforderlich",
"1853f9414bc61ed196952f5c81e41802": "Eigenschaftsnamen, die einen Punkt enthalten, werden nicht unterstützt. Modell: {0}, dynamische Eigenschaft: {1}",
"bdb11cc1c780c9ccac33c316cfdc9d82": "Typ nicht definiert für Eigenschaft {0}.{1}",
"cd930369e86cdd222f7bd117c6f9fa94": "Unbekannter Standardwertprovider {0}",
"0c0b867aca0973ba26e887d3337cc4ec": "{{Polymorphic}}-Modell nicht gefunden: '{0}' nicht festgelegt",
"2f062cbecdf24245731bddc77714c814": "\"{0}\"-Beziehung für {1} konnte nicht gefunden werden",
"3cde8cc9bca22c67278b202ab0720106": "Keine Instanz mit ID {0} für {1} gefunden",
"6502a117987610380b9068ef98b1b0ee": "Kein Datensatz gefunden in {0} für ({1}.{2} ,{3}.{4})",
"6fcc2ff0db7a4f490f5e0ce9e24691f3": "{{HasOne}}-Beziehung kann nicht mehr als eine Instanz von {0} erstellen",
"7e9530c0399289be0ee601a604be71ff": "{{BelongsTo}}-Beziehung {0} ist leer",
"7faa840eb6ce11250a141deb42a6c489": "Unbekannter {{scope}} für Beziehung: {0}",
"89afd3a9249f5a8d3edda07d84ca049d": "{{Polymorphic}}-Modell nicht gefunden: '{0}'",
"a004f310d315e592843776fab964eaeb": "{{Polymorphic}}-Beziehungen erfordern ein through-Modell",
"a25e41a39c60c4702e55d0c3936576a1": "Schlüssel stimmen nicht überein: {0}.{1}: {2}, {3}.{4}: {5}",
"a327355560d495454fba2c1aad6bdf09": "Unbekannte Bereichsmethode: {0}",
"e08ab0e1ab55f26c357061447b635905": "Keine Beziehung gefunden in {0} für ({1}.{2} ,{3}.{4})",
"e55937649d8d7a11706b8cec22d02eae": "{{HasOne}}-Beziehung {0} ist leer",
"2c4904377a87fdab502118719cc0d266": "{{Transaction}} wird nicht unterstützt",
"ecb7aa804bf54c682999d20d6436104c": "Die {{transaction}} ist nicht aktiv: {0}",
"89bf6d92731fe7bd2146ce8d0bec205c": "Ungültiges Argument, muss eine Zeichenfolge, ein {{regex}}-Literalwert oder ein {{RegExp}}-Objekt sein",
"8c5ab01638c1ac1d58168c6346a8481a": "Ungültige {{regex}}-Flags: {0}",
"a6c18a7f4390cd3d59a2a7a047ae2aab": "Führen Sie den Befehl \"{{npm install loopback-datasource-juggler}} {0}\" aus ",
"b138294f132edfe1eb2a8211150c7238": "Unerwartetes 'nicht definiert' in Abfrage",
"8a39126103a157f501affa070367a1b0": "Die Instanz {0} ist nicht gültig. Details: {1}."
}

73
intl/en/messages.json Normal file
View File

@ -0,0 +1,73 @@
{
"0bd753a8944ad0af85a939bb25273887": "Cannot expire unknown key {0}",
"bec226891a505828bfc76c5cfd73b336": "Cannot get TTL for unknown key {0}",
"4c78325cedbb826db3a05bf5df0e8546": "You must provide an {{id}} when replacing!",
"a0cf0e09c26df14283223e84e6a10f00": "Could not update attributes. {{Object}} with {{id}} {0} does not exist!",
"e54d944c2a2c85a23caa86027ae307cf": "Cannot migrate models not attached to this datasource: {0}",
"e6161ae8459c79d810e2aa9d21282a39": "You must provide an {{id}} when updating attributes!",
"fca4d12faff1035d9d0438d73432571b": "Duplicate entry for {0}.{1}",
"09483e03b91c8bd58732a74b3ef1ec13": "Invalid date: {0}",
"0b16d3ffc42f91b4b9a4b3b50c41c838": "The order {0} is not valid",
"0e88a84c6bc2638fbcc5d3ea95181f99": "Unknown property: {0}",
"21095484501dbff31af6556fa6039182": "The {{offset/skip}} parameter {0} is not valid",
"416dfbb7b823f51c9f3800be81060b41": "No instance with {{id}} {0} found for {1}",
"4e31b1edd10dadb724d83387de0b5062": "The {{limit}} parameter {0} is not valid",
"5ec8efeb715a2c34b440f2d76e2cf87d": " {0}",
"62a2d80c405b7fec5f547c448ab1b6ff": "The {{order}} {0} has invalid direction",
"63f503acabdc4c7588aab741b8466862": "The {0} property has invalid clause {1}",
"67c2bf43b5281ab929617423ea8a6f3e": "The connector {0} does not support {{replaceById}} operation. This is not a bug in LoopBack. Please contact the authors of the connector, preferably via GitHub issues.",
"6c3234937d69763fc7f6bcafccc59bbc": "{{Model::deleteById}} requires the {{id}} argument",
"6eb6fd4fbd73394000bc25f5776fd20c": "{{Model::exists}} requires the {{id}} argument",
"7bbbdece4eea90e42aa5c0bce295e503": "{{Model::findById}} requires the {{id}} argument",
"9da58ab4555ac50f4999883de6470d7d": "The {0} operator has invalid clauses {1}",
"a829dee089c912e68c18920ba015400c": "WARNING: {{id}} property cannot be changed from {0} to {1} for model:{2} in {{'loaded'}} operation hook",
"a984a076c59e451948b2bcf7a393d860": "WARNING: {{id}} property cannot be changed from {0} to {1} for model:{2} in {{'before save'}} operation hook",
"baf2c8b0c5a574b8a894e9b6304fece1": "The where clause {0} is not an {{object}}",
"db03083e9a768388fdbee865249ac67a": "Ignoring validation errors in {{updateOrCreate()}}:",
"e4434de4bb8f5a3cd1d416e4d80d7e0b": "Unknown \"{0}\" {{id}} \"{1}\".",
"e54f118a75e15e132f16b985274eb46d": "The query filter {0} is not an {{object}}",
"f6e8c96c93b9c7687d6c172b3695e898": "{{id}} property ({0}) cannot be updated from {1} to {2}",
"0be2d39d225b1d8b2a0f92ad5c65c9ac": "No model specified for {{polymorphic}} {0}: {1}",
"2f4af31c144bbfab1bbf479866acd820": "\nWARNING: {{LoopBack}} connector \"{0}\" is not installed as any of the following modules:\n\n {1}\n\nTo fix, run:\n\n {{npm install {2} --save}}\n",
"6111399276924ffa3bc9a410cdfcb2e5": "No {{id}} name {0}",
"75518263527c423b8a12f908a2730b0f": "{{className}} is required",
"791ab3031a73ede03f7d6299a85e8289": "Timeout in connecting after {0} ms",
"7b277018e43d41bc445731092b91547d": "Not connected",
"a2487abefef4259c2131d96cdb8543b1": "Connection fails: {0}\nIt will be retried for the next request.",
"b15b20280211ad258d92947f05b6e4a5": "The connector has not been initialized.",
"e0e9504e137a3c3339144b51ed76fef2": "Connector is not defined correctly: it should create `{{connector}}` member of dataSource",
"ec42dca074f1818c447f7ad16e2d01af": "{0} is not provided by the attached connector",
"ba0fd8106eb54de4d003a844206431fd": "Model hook \"{0}\" is deprecated, use Operation hooks instead. {{http://docs.strongloop.com/display/LB/Operation+hooks}}",
"280f4550f90e133118955ec6f6f72830": "Discriminator type {0} specified but no model exists with such name",
"38dbf42c29a4645238cc3d632e88ebc9": "{{Relation.modelTo}} is not defined for relation {0} and is no {{polymorphic}}",
"eb56c2b0c30cf006e2df00a549ec9c2c": "Relation \"{0}\" is not defined for {1} model",
"938401ea4ce48159efa9be1d4a5e8bab": "Items must be an array: {0}",
"f41bd91dc0f000a79c0bf842f1b7fdf9": "could not create List from JSON string: {0}",
"514985b2327f061ffb1c932f6b909979": "Model {0} is not defined.",
"cc09e361de7b0f3dc1687515c5713885": "Property names containing a dot are not supported. Model: {0}, property: {1}",
"da02dd6c53d4148320eeb31718a7aebe": "Invalid type for property {0}",
"ddf0aa14803f1c84f4a97f3803f7471c": "Class name required",
"1853f9414bc61ed196952f5c81e41802": "Property names containing a dot are not supported. Model: {0}, dynamic property: {1}",
"bdb11cc1c780c9ccac33c316cfdc9d82": "Type not defined for property {0}.{1}",
"cd930369e86cdd222f7bd117c6f9fa94": "Unknown default value provider {0}",
"0c0b867aca0973ba26e887d3337cc4ec": "{{Polymorphic}} model not found: `{0}` not set",
"2f062cbecdf24245731bddc77714c814": "Could not find \"{0}\" relation for {1}",
"3cde8cc9bca22c67278b202ab0720106": "No instance with id {0} found for {1}",
"6502a117987610380b9068ef98b1b0ee": "No record found in {0} for ({1}.{2} ,{3}.{4})",
"6fcc2ff0db7a4f490f5e0ce9e24691f3": "{{HasOne}} relation cannot create more than one instance of {0}",
"7e9530c0399289be0ee601a604be71ff": "{{BelongsTo}} relation {0} is empty",
"7faa840eb6ce11250a141deb42a6c489": "Unknown relation {{scope}}: {0}",
"89afd3a9249f5a8d3edda07d84ca049d": "{{Polymorphic}} model not found: `{0}`",
"a004f310d315e592843776fab964eaeb": "{{Polymorphic}} relations need a through model",
"a25e41a39c60c4702e55d0c3936576a1": "Key mismatch: {0}.{1}: {2}, {3}.{4}: {5}",
"a327355560d495454fba2c1aad6bdf09": "Unknown scope method: {0}",
"e08ab0e1ab55f26c357061447b635905": "No relation found in {0} for ({1}.{2},{3}.{4})",
"e55937649d8d7a11706b8cec22d02eae": "{{HasOne}} relation {0} is empty",
"2c4904377a87fdab502118719cc0d266": "{{Transaction}} is not supported",
"ecb7aa804bf54c682999d20d6436104c": "The {{transaction}} is not active: {0}",
"89bf6d92731fe7bd2146ce8d0bec205c": "Invalid argument, must be a string, {{regex}} literal, or {{RegExp}} object",
"8c5ab01638c1ac1d58168c6346a8481a": "Invalid {{regex}} flags: {0}",
"a6c18a7f4390cd3d59a2a7a047ae2aab": "Run \"{{npm install loopback-datasource-juggler}} {0}\" command ",
"b138294f132edfe1eb2a8211150c7238": "Unexpected `undefined` in query",
"8a39126103a157f501affa070367a1b0": "The {0} instance is not valid. Details: {1}."
}

74
intl/es/messages.json Normal file
View File

@ -0,0 +1,74 @@
{
"0bd753a8944ad0af85a939bb25273887": "La clave desconocida {0} no puede caducar",
"bec226891a505828bfc76c5cfd73b336": "No se puede obtener TTL para la clave desconocida {0}",
"4c78325cedbb826db3a05bf5df0e8546": "Debe proporcionar un {{id}} al sustituir.",
"a0cf0e09c26df14283223e84e6a10f00": "No se han podido actualizar atributos. {{Object}} con {{id}} {0} no existe.",
"e54d944c2a2c85a23caa86027ae307cf": "No se pueden migrar modelos no conectados a este origen de datos: {0}",
"e6161ae8459c79d810e2aa9d21282a39": "Debe proporcionar un {{id}} al actualizar atributos.",
"fca4d12faff1035d9d0438d73432571b": "Entrada duplicada para {0}.{1}",
"09483e03b91c8bd58732a74b3ef1ec13": "Fecha no válida: {0}",
"0b16d3ffc42f91b4b9a4b3b50c41c838": "El orden {0} no es válido",
"0e88a84c6bc2638fbcc5d3ea95181f99": "Propiedad desconocida: {0}",
"21095484501dbff31af6556fa6039182": "El parámetro de {{offset/skip}} {0} no es válido",
"416dfbb7b823f51c9f3800be81060b41": "No se ha encontrado ninguna instancia con el {{id}} {0} para {1}",
"4e31b1edd10dadb724d83387de0b5062": "El parámetro de {{limit}} {0} no es válido",
"5ec8efeb715a2c34b440f2d76e2cf87d": " {0}",
"62a2d80c405b7fec5f547c448ab1b6ff": "El {{order}} {0} tiene una dirección no válida",
"63f503acabdc4c7588aab741b8466862": "La propiedad {0} tiene una cláusula {1} no válida",
"67c2bf43b5281ab929617423ea8a6f3e": "El conector {0} no admite la operación {{replaceById}}. Esto no es un error en LoopBack. Póngase en contacto con los autores del conector, preferiblemente a través de cuestiones GitHub.",
"6c3234937d69763fc7f6bcafccc59bbc": "{{Model::deleteById}} requiere el argumento {{id}}",
"6eb6fd4fbd73394000bc25f5776fd20c": "{{Model::exists}} requiere el argumento {{id}}",
"7bbbdece4eea90e42aa5c0bce295e503": "{{Model::findById}} requiere el argumento {{id}}",
"9da58ab4555ac50f4999883de6470d7d": "El operador {0} contiene cláusulas {1} no válidas",
"a829dee089c912e68c18920ba015400c": "AVISO: la propiedad {{id}} no puede cambiarse de {0} a {1} para el modelo:{2} en el gancho de operación {{'loaded'}}",
"a984a076c59e451948b2bcf7a393d860": "AVISO: la propiedad {{id}} no puede cambiarse de {0} a {1} para el modelo:{2} en el gancho de operación {{'before save'}}",
"baf2c8b0c5a574b8a894e9b6304fece1": "La cláusula where {0} no es un {{object}}",
"db03083e9a768388fdbee865249ac67a": "Se ignoran los errores de validación en {{updateOrCreate()}}:",
"e4434de4bb8f5a3cd1d416e4d80d7e0b": "{{id}} de \"{0}\" desconocido \"{1}\".",
"e54f118a75e15e132f16b985274eb46d": "El filtro de consultas {0} no es un {{object}}",
"f6e8c96c93b9c7687d6c172b3695e898": "La propiedad de {{id}} ({0}) no puede actualizarse de {1} a {2}",
"0be2d39d225b1d8b2a0f92ad5c65c9ac": "No se ha especificado ningún modelo para {{polymorphic}} {0}: {1}",
"2f4af31c144bbfab1bbf479866acd820": "\nAVISO: el conector {{LoopBack}} \"{0}\" no está instalado como ninguno de los módulos siguientes:\n\n {1}\n\nPara solucionarlo, ejecute:\n\n {{npm install {2} --save}}\n",
"6111399276924ffa3bc9a410cdfcb2e5": "No hay ningún nombre de {{id}} {0}",
"75518263527c423b8a12f908a2730b0f": "{{className}} es obligatorio",
"791ab3031a73ede03f7d6299a85e8289": "Tiempo de espera agotado al conectarse después de {0} ms",
"7b277018e43d41bc445731092b91547d": "No conectado",
"a2487abefef4259c2131d96cdb8543b1": "La conexión falla: {0}\nSe reintentará en la siguiente solicitud.",
"b15b20280211ad258d92947f05b6e4a5": "El conector no se ha inicializado.",
"e0e9504e137a3c3339144b51ed76fef2": "El conector no está definido correctamente: debe crear el miembro `{{connector}}` de dataSource",
"ec42dca074f1818c447f7ad16e2d01af": "El conector asociado no ha proporcionado {0}",
"ba0fd8106eb54de4d003a844206431fd": "El gancho de modelo \"{0}\" está en desuso, utilice ganchos de operación en su lugar. {{http://docs.strongloop.com/display/LB/Operation+hooks}}",
"280f4550f90e133118955ec6f6f72830": "Se ha especificado el tipo de discriminador {0}, pero no existe ningún modelo con ese nombre",
"38dbf42c29a4645238cc3d632e88ebc9": "{{Relation.modelTo}} no se ha definido para la relación {0} y no es {{polymorphic}}",
"eb56c2b0c30cf006e2df00a549ec9c2c": "La relación \"{0}\" no está definida para el modelo {1}",
"938401ea4ce48159efa9be1d4a5e8bab": "Items debe ser una matriz: {0}",
"f41bd91dc0f000a79c0bf842f1b7fdf9": "no se ha podido crear la lista a partir de la serie JSON: {0}",
"514985b2327f061ffb1c932f6b909979": "El modelo {0} no se ha definido.",
"cc09e361de7b0f3dc1687515c5713885": "Los nombres de propiedad que contienen un punto no están soportados. Modelo: {0}, propiedad: {1}",
"da02dd6c53d4148320eeb31718a7aebe": "Tipo no válido para la propiedad {0}",
"ddf0aa14803f1c84f4a97f3803f7471c": "Nombre de clase necesario",
"1853f9414bc61ed196952f5c81e41802": "Los nombres de propiedad que contienen un punto no están soportados. Modelo: {0}, propiedad dinámica: {1}",
"bdb11cc1c780c9ccac33c316cfdc9d82": "Tipo no definido para la propiedad {0}.{1}",
"cd930369e86cdd222f7bd117c6f9fa94": "Proveedor de valor predeterminado desconocido {0}",
"0c0b867aca0973ba26e887d3337cc4ec": "Modelo {{Polymorphic}} no encontrado: `{0}` no establecido",
"2f062cbecdf24245731bddc77714c814": "No se ha encontrado la relación \"{0}\" para {1}",
"3cde8cc9bca22c67278b202ab0720106": "No se ha encontrado ninguna instancia con el ID {0} para {1}",
"6502a117987610380b9068ef98b1b0ee": "No se ha encontrado ningún registro en {0} para ({1}.{2} ,{3}.{4})",
"6fcc2ff0db7a4f490f5e0ce9e24691f3": "La relación {{HasOne}} no puede crear más de una instancia de {0}",
"7e9530c0399289be0ee601a604be71ff": "la relación {{BelongsTo}} {0} está vacía",
"7faa840eb6ce11250a141deb42a6c489": "Relación desconocida {{scope}}: {0}",
"89afd3a9249f5a8d3edda07d84ca049d": "Modelo {{Polymorphic}} no encontrado: `{0}`",
"a004f310d315e592843776fab964eaeb": "Las relaciones {{Polymorphic}} necesitan un modelo definido",
"a25e41a39c60c4702e55d0c3936576a1": "Discrepancia de claves: {0}.{1}: {2}, {3}.{4}: {5}",
"a327355560d495454fba2c1aad6bdf09": "Método de ámbito desconocido: {0}",
"e08ab0e1ab55f26c357061447b635905": "No se ha encontrado ninguna relación en {0} para ({1}.{2},{3}.{4})",
"e55937649d8d7a11706b8cec22d02eae": "la relación {{HasOne}} {0} está vacía",
"2c4904377a87fdab502118719cc0d266": "{{Transaction}} no está soportada.",
"ecb7aa804bf54c682999d20d6436104c": "La {{transaction}} no está activa: {0}",
"89bf6d92731fe7bd2146ce8d0bec205c": "Argumento no válido, debe ser una serie, literal de {{regex}} u objeto {{RegExp}}",
"8c5ab01638c1ac1d58168c6346a8481a": "Distintivos de {{regex}} no válidos: {0}",
"a6c18a7f4390cd3d59a2a7a047ae2aab": "Ejecute el mandato \"{{npm install loopback-datasource-juggler}} {0}\" ",
"b138294f132edfe1eb2a8211150c7238": "`undefined` inesperado en la consulta",
"8a39126103a157f501affa070367a1b0": "La instancia {0} no es válida. Detalles: {1}."
}

74
intl/fr/messages.json Normal file
View File

@ -0,0 +1,74 @@
{
"0bd753a8944ad0af85a939bb25273887": "Impossible de faire expirer la clé inconnue {0}",
"bec226891a505828bfc76c5cfd73b336": "Impossible d'obtenir TTL pour la clé inconnue {0}",
"4c78325cedbb826db3a05bf5df0e8546": "Vous devez fournir un {{id}} lors du remplacement !",
"a0cf0e09c26df14283223e84e6a10f00": "Impossible de mettre à jour les attributs. {{Object}} avec {{id}} {0} n'existe pas !",
"e54d944c2a2c85a23caa86027ae307cf": "Impossible de migrer les modèles non associés à cette source de données : {0}",
"e6161ae8459c79d810e2aa9d21282a39": "Vous devez fournir un {{id}} lors de la mise à jour des attributs !",
"fca4d12faff1035d9d0438d73432571b": "Entrée en double pour {0}.{1}",
"09483e03b91c8bd58732a74b3ef1ec13": "Date non valide : {0}",
"0b16d3ffc42f91b4b9a4b3b50c41c838": "L'ordre {0} n'est pas valide",
"0e88a84c6bc2638fbcc5d3ea95181f99": "Propriété inconnue : {0}",
"21095484501dbff31af6556fa6039182": "Le paramètre {{offset/skip}} {0} n'est pas valide",
"416dfbb7b823f51c9f3800be81060b41": "Aucune instance avec {{id}} {0} trouvée pour {1}",
"4e31b1edd10dadb724d83387de0b5062": "Le paramètre {{limit}} {0} n'est pas valide",
"5ec8efeb715a2c34b440f2d76e2cf87d": " {0}",
"62a2d80c405b7fec5f547c448ab1b6ff": "{{order}} {0} a une direction non valide",
"63f503acabdc4c7588aab741b8466862": "La propriété {0} a la clause non valide {1}",
"67c2bf43b5281ab929617423ea8a6f3e": "Le connecteur {0} ne prend pas en charge l'opération {{replaceById}}. IL ne s'agit pas d'un bogue dans LoopBack. Prenez contact avec les auteurs du connecteur, de préférence via les incidents GitHub.",
"6c3234937d69763fc7f6bcafccc59bbc": "{{Model::deleteById}} requiert l'argument {{id}}",
"6eb6fd4fbd73394000bc25f5776fd20c": "{{Model::exists}} requiert l'argument {{id}}",
"7bbbdece4eea90e42aa5c0bce295e503": "{{Model::findById}} requiert l'argument {{id}}",
"9da58ab4555ac50f4999883de6470d7d": "L'opérateur {0} a les clauses non valides {1}",
"a829dee089c912e68c18920ba015400c": "AVERTISSEMENT : la propriété {{id}} ne peut pas être modifiée de {0} en {1} pour le modèle :{2} dans le point d'ancrage d'opération {{'loaded'}}",
"a984a076c59e451948b2bcf7a393d860": "AVERTISSEMENT : la propriété {{id}} ne peut pas être modifiée de {0} en {1} pour le modèle :{2} dans le point d'ancrage d'opération {{'before save'}}",
"baf2c8b0c5a574b8a894e9b6304fece1": "La clause where {0} n'est pas un {{object}}",
"db03083e9a768388fdbee865249ac67a": "Erreurs de validation ignorées dans {{updateOrCreate()}} :",
"e4434de4bb8f5a3cd1d416e4d80d7e0b": "\"{0}\" {{id}} \"{1}\" inconnu.",
"e54f118a75e15e132f16b985274eb46d": "Le filtre de requête {0} n'est pas un {{object}}",
"f6e8c96c93b9c7687d6c172b3695e898": "La propriété {{id}} ({0}) ne peut pas être mise à jour à partir de {1} vers {2}",
"0be2d39d225b1d8b2a0f92ad5c65c9ac": "Aucun modèle spécifié pour {{polymorphic}} {0} : {1}",
"2f4af31c144bbfab1bbf479866acd820": "\nAVERTISSEMENT : Le connecteur {{LoopBack}} \"{0}\" n'est pas installé, tout comme les modules suivants :\n\n {1}\n\nPour corriger le problème, exécutez :\n\n {{npm install {2} --save}}\n",
"6111399276924ffa3bc9a410cdfcb2e5": "Aucun nom {{id}} {0}",
"75518263527c423b8a12f908a2730b0f": "{{className}} est obligatoire",
"791ab3031a73ede03f7d6299a85e8289": "Expiration du délai de connexion après {0} ms",
"7b277018e43d41bc445731092b91547d": "Non connecté",
"a2487abefef4259c2131d96cdb8543b1": "Echec de la connexion : {0}\nUn nouvel essai sera effectué pour la demande suivante.",
"b15b20280211ad258d92947f05b6e4a5": "Le connecteur n'a pas été initialisé.",
"e0e9504e137a3c3339144b51ed76fef2": "Le connecteur n'est pas défini correctement ; il devrait créer le membre `{{connector}}` de la source de données",
"ec42dca074f1818c447f7ad16e2d01af": "{0} n'est pas fourni par le connecteur associé",
"ba0fd8106eb54de4d003a844206431fd": "Le point d'ancrage de modèle \"{0}\" est obsolète ; utilisez à la place les points d'ancrage d'opération. {{http://docs.strongloop.com/display/LB/Operation+hooks}}",
"280f4550f90e133118955ec6f6f72830": "Le type de discriminateur {0} est indiqué mais il n'existe pas de modèle de ce nom",
"38dbf42c29a4645238cc3d632e88ebc9": "{{Relation.modelTo}} n'est pas défini pour la relation {0} et il n'est pas {{polymorphic}}",
"eb56c2b0c30cf006e2df00a549ec9c2c": "La relation \"{0}\" n'est pas définie pour le modèle {1}",
"938401ea4ce48159efa9be1d4a5e8bab": "Les éléments doivent être un tableau : {0}",
"f41bd91dc0f000a79c0bf842f1b7fdf9": "impossible de créer la liste à partir de la chaîne JSON : {0}",
"514985b2327f061ffb1c932f6b909979": "Le modèle {0} n'est pas défini.",
"cc09e361de7b0f3dc1687515c5713885": "Les noms de propriété contentant un point ne sont pas pris en charge. Modèle : {0}, propriété : {1}",
"da02dd6c53d4148320eeb31718a7aebe": "Type non valide pour la propriété {0}",
"ddf0aa14803f1c84f4a97f3803f7471c": "Nom de classe obligatoire",
"1853f9414bc61ed196952f5c81e41802": "Les noms de propriété contentant un point ne sont pas pris en charge. Modèle : {0}, propriété dynamique : {1}",
"bdb11cc1c780c9ccac33c316cfdc9d82": "Type non défini pour la propriété {0}.{1}",
"cd930369e86cdd222f7bd117c6f9fa94": "Fournisseur de valeur par défaut inconnu {0}",
"0c0b867aca0973ba26e887d3337cc4ec": "Modèle {{Polymorphic}} non trouvé : `{0}` non défini",
"2f062cbecdf24245731bddc77714c814": "Relation \"{0}\" introuvable pour {1}",
"3cde8cc9bca22c67278b202ab0720106": "Aucune instance avec l'id {0} trouvée pour {1}",
"6502a117987610380b9068ef98b1b0ee": "Aucun enregistrement trouvé dans {0} pour ({1}.{2} ,{3}.{4})",
"6fcc2ff0db7a4f490f5e0ce9e24691f3": "La relation {{HasOne}} ne peut pas créer plus d'une instance de {0}",
"7e9530c0399289be0ee601a604be71ff": "La relation {{BelongsTo}} {0} est vide",
"7faa840eb6ce11250a141deb42a6c489": "Relation inconnue {{scope}} : {0}",
"89afd3a9249f5a8d3edda07d84ca049d": "Modèle {{Polymorphic}} introuvable : `{0}`",
"a004f310d315e592843776fab964eaeb": "Les relations {{Polymorphic}} ont besoin d'un modèle exhaustif",
"a25e41a39c60c4702e55d0c3936576a1": "Non-concordance de clé : {0}.{1}: {2}, {3}.{4}: {5}",
"a327355560d495454fba2c1aad6bdf09": "Méthode scope inconnue : {0}",
"e08ab0e1ab55f26c357061447b635905": "Aucune relation trouvée dans {0} pour ({1}.{2},{3}.{4})",
"e55937649d8d7a11706b8cec22d02eae": "La relation {{HasOne}} {0} est vide",
"2c4904377a87fdab502118719cc0d266": "{{Transaction}} n'est pas pris en charge",
"ecb7aa804bf54c682999d20d6436104c": "{{transaction}} n'est pas actif : {0}",
"89bf6d92731fe7bd2146ce8d0bec205c": "Argument non valide ; doit être une chaîne, un littéral {{regex}} ou un objet {{RegExp}}",
"8c5ab01638c1ac1d58168c6346a8481a": "Indicateurs {{regex}} non valides : {0}",
"a6c18a7f4390cd3d59a2a7a047ae2aab": "Exécuter la commande \"{{npm install loopback-datasource-juggler}} {0}\" ",
"b138294f132edfe1eb2a8211150c7238": "`undefined` inattendu dans la requête",
"8a39126103a157f501affa070367a1b0": "L'instance {0} n'est pas valide. Détails : {1}."
}

74
intl/it/messages.json Normal file
View File

@ -0,0 +1,74 @@
{
"0bd753a8944ad0af85a939bb25273887": "Impossibile fare scadere la chiave sconosciuta {0}",
"bec226891a505828bfc76c5cfd73b336": "Impossibile acquisire TTL per la chiave sconosciuta {0}",
"4c78325cedbb826db3a05bf5df0e8546": "È necessario fornire un {{id}} durante la sostituzione.",
"a0cf0e09c26df14283223e84e6a10f00": "Impossibile aggiornare gli attributi. {{Object}} con {{id}} {0} non esiste.",
"e54d944c2a2c85a23caa86027ae307cf": "Impossibile migrare i modelli non allegati a questa origine dati: {0}",
"e6161ae8459c79d810e2aa9d21282a39": "È necessario fornire un {{id}} durante l'aggiornamento degli attributi.",
"fca4d12faff1035d9d0438d73432571b": "Voce duplicata per {0}.{1}",
"09483e03b91c8bd58732a74b3ef1ec13": "Data non valida: {0}",
"0b16d3ffc42f91b4b9a4b3b50c41c838": "L'ordine {0} non è valido",
"0e88a84c6bc2638fbcc5d3ea95181f99": "Proprietà sconosciuta: {0}",
"21095484501dbff31af6556fa6039182": "Il parametro {{offset/skip}} {0} non è valido",
"416dfbb7b823f51c9f3800be81060b41": "Nessuna istanza con {{id}} {0} trovata per {1}",
"4e31b1edd10dadb724d83387de0b5062": "Il parametro {{limit}} {0} non è valido",
"5ec8efeb715a2c34b440f2d76e2cf87d": " {0}",
"62a2d80c405b7fec5f547c448ab1b6ff": "{{order}} {0} ha una direzione non valida",
"63f503acabdc4c7588aab741b8466862": "La proprietà {0} ha una clausola non valida {1}",
"67c2bf43b5281ab929617423ea8a6f3e": "Il connettore {0} non supporta l'operazione {{replaceById}}. Questo non è un bug in LoopBack. Contattare gli autori del connettore, preferibilmente mediante GitHub.",
"6c3234937d69763fc7f6bcafccc59bbc": "{{Model::deleteById}} richiede l'argomento {{id}}",
"6eb6fd4fbd73394000bc25f5776fd20c": "{{Model::exists}} richiede l'argomento {{id}}",
"7bbbdece4eea90e42aa5c0bce295e503": "{{Model::findById}} richiede l'argomento {{id}}",
"9da58ab4555ac50f4999883de6470d7d": "L'operatore {0} ha clausole non valide {1}",
"a829dee089c912e68c18920ba015400c": "AVVERTENZA: impossibile modificare la proprietà {{id}} da {0} a {1} per il modello:{2} nell'hook operazione {{'loaded'}}",
"a984a076c59e451948b2bcf7a393d860": "AVVERTENZA: impossibile modificare la proprietà {{id}} da {0} a {1} per il modello:{2} nell'hook operazione {{'before save'}}",
"baf2c8b0c5a574b8a894e9b6304fece1": "La clausola where {0} non è un {{object}}",
"db03083e9a768388fdbee865249ac67a": "Errore di convalida in {{updateOrCreate()}} ignorati:",
"e4434de4bb8f5a3cd1d416e4d80d7e0b": "{{id}} \"{0}\" sconosciuto \"{1}\".",
"e54f118a75e15e132f16b985274eb46d": "Il filtro della query {0} non è un {{object}}",
"f6e8c96c93b9c7687d6c172b3695e898": "Impossibile aggiornare la proprietà {{id}} ({0}) da {1} a {2}",
"0be2d39d225b1d8b2a0f92ad5c65c9ac": "Nessun modello specificato per {{polymorphic}} {0}: {1}",
"2f4af31c144bbfab1bbf479866acd820": "\nAVVERTENZA: il connettore {{LoopBack}} \"{0}\" non è installato come nessuno dei seguenti moduli:\n\n {1}\n\nPer correggere, eseguire:\n\n {{npm install {2} --save}}\n",
"6111399276924ffa3bc9a410cdfcb2e5": "Nessun nome {{id}} {0}",
"75518263527c423b8a12f908a2730b0f": "{{className}} è obbligatorio",
"791ab3031a73ede03f7d6299a85e8289": "Timeout nella connessione dopo {0} ms",
"7b277018e43d41bc445731092b91547d": "Non connesso",
"a2487abefef4259c2131d96cdb8543b1": "Errore della connessione: {0}\nVerrà eseguito un nuovo tentativo per la richiesta successiva.",
"b15b20280211ad258d92947f05b6e4a5": "Il connettore non è stato inizializzato.",
"e0e9504e137a3c3339144b51ed76fef2": "Il connettore non è definito correttamente: deve creare il membro `{{connector}}` di dataSource",
"ec42dca074f1818c447f7ad16e2d01af": "{0} non fornito dal connettore collegato",
"ba0fd8106eb54de4d003a844206431fd": "L'hook del modello \"{0}\" è obsoleto, utilizzare gli hook dell'operazione. {{http://docs.strongloop.com/display/LB/Operation+hooks}}",
"280f4550f90e133118955ec6f6f72830": "È stato specificato il tipo di discriminatore {0} ma non esiste alcun modello con tale nome",
"38dbf42c29a4645238cc3d632e88ebc9": "{{Relation.modelTo}} non è definito per la relazione {0} e non è {{polymorphic}}",
"eb56c2b0c30cf006e2df00a549ec9c2c": "Relazione \"{0}\" non definita per il modello {1}",
"938401ea4ce48159efa9be1d4a5e8bab": "Gli elementi devono essere un array: {0}",
"f41bd91dc0f000a79c0bf842f1b7fdf9": "impossibile creare un elenco dalla stringa JSON: {0}",
"514985b2327f061ffb1c932f6b909979": "Il modello {0} non è definito.",
"cc09e361de7b0f3dc1687515c5713885": "I nomi delle proprietà che contengono un punto non sono supportati. Modello: {0}, proprietà: {1}",
"da02dd6c53d4148320eeb31718a7aebe": "Tipo non valido per la proprietà {0}",
"ddf0aa14803f1c84f4a97f3803f7471c": "Nome della classe obbligatorio",
"1853f9414bc61ed196952f5c81e41802": "I nomi delle proprietà che contengono un punto non sono supportati. Modello: {0}, proprietà dinamica: {1}",
"bdb11cc1c780c9ccac33c316cfdc9d82": "Tipo non definito per la proprietà {0}.{1}",
"cd930369e86cdd222f7bd117c6f9fa94": "Provider del valore predefinito sconosciuto {0}",
"0c0b867aca0973ba26e887d3337cc4ec": "Modello {{Polymorphic}} non trovato: `{0}` non impostato",
"2f062cbecdf24245731bddc77714c814": "Impossibile trovare la relazione \"{0}\" per {1}",
"3cde8cc9bca22c67278b202ab0720106": "Nessuna istanza con ID {0} trovata per {1}",
"6502a117987610380b9068ef98b1b0ee": "Nessun record trovato in {0} per ({1}.{2} ,{3}.{4})",
"6fcc2ff0db7a4f490f5e0ce9e24691f3": "La relazione {{HasOne}} non può creare più di una istanza di {0}",
"7e9530c0399289be0ee601a604be71ff": "La relazione {{BelongsTo}} {0} è vuota",
"7faa840eb6ce11250a141deb42a6c489": "Relazione sconosciuta {{scope}}: {0}",
"89afd3a9249f5a8d3edda07d84ca049d": "Modello {{Polymorphic}} non trovato: `{0}`",
"a004f310d315e592843776fab964eaeb": "Le relazioni {{Polymorphic}} richiedono un modello di passaggio",
"a25e41a39c60c4702e55d0c3936576a1": "Mancata corrispondenza della chiave: {0}.{1}: {2}, {3}.{4}: {5}",
"a327355560d495454fba2c1aad6bdf09": "Metodo dell'ambito sconosciuto: {0}",
"e08ab0e1ab55f26c357061447b635905": "Nessuna relazione trovata in {0} per ({1}.{2},{3}.{4})",
"e55937649d8d7a11706b8cec22d02eae": "La relazione {{HasOne}} {0} è vuota",
"2c4904377a87fdab502118719cc0d266": "{{Transaction}} non supportata",
"ecb7aa804bf54c682999d20d6436104c": "La {{transaction}} non è attiva: {0}",
"89bf6d92731fe7bd2146ce8d0bec205c": "Argomento non valido, deve essere una stringa, un valore letterale {{regex}} o un oggetto {{RegExp}}",
"8c5ab01638c1ac1d58168c6346a8481a": "Indicatori {{regex}} non validi: {0}",
"a6c18a7f4390cd3d59a2a7a047ae2aab": "Eseguire il comando \"{{npm install loopback-datasource-juggler}} {0}\" ",
"b138294f132edfe1eb2a8211150c7238": "Elemento `undefined` non previsto nella query",
"8a39126103a157f501affa070367a1b0": "L'istanza {0} non è valida. Dettagli: {1}."
}

74
intl/ja/messages.json Normal file
View File

@ -0,0 +1,74 @@
{
"0bd753a8944ad0af85a939bb25273887": "不明のキー {0} を期限切れにすることができません",
"bec226891a505828bfc76c5cfd73b336": "不明のキー {0} の TTL を取得できません",
"4c78325cedbb826db3a05bf5df0e8546": "置換するときは {{id}} を指定する必要があります。",
"a0cf0e09c26df14283223e84e6a10f00": "属性を更新できませんでした。 {{id}} {0} の {{Object}} は存在しません。",
"e54d944c2a2c85a23caa86027ae307cf": "このデータ・ソースに付加されていないモデルはマイグレーションできません: {0}",
"e6161ae8459c79d810e2aa9d21282a39": "属性を更新するときは {{id}} を指定する必要があります。",
"fca4d12faff1035d9d0438d73432571b": "{0}.{1} のエントリーが重複しています",
"09483e03b91c8bd58732a74b3ef1ec13": "無効な日付: {0}",
"0b16d3ffc42f91b4b9a4b3b50c41c838": "順序 {0} が無効です",
"0e88a84c6bc2638fbcc5d3ea95181f99": "不明なプロパティー: {0}",
"21095484501dbff31af6556fa6039182": "{{offset/skip}} パラメーター {0} は無効です",
"416dfbb7b823f51c9f3800be81060b41": "{1} に関する {{id}} {0} のインスタンスが見つかりません",
"4e31b1edd10dadb724d83387de0b5062": "{{limit}} パラメーター {0} は無効です",
"5ec8efeb715a2c34b440f2d76e2cf87d": " {0}",
"62a2d80c405b7fec5f547c448ab1b6ff": "{{order}} {0} の方向が無効です",
"63f503acabdc4c7588aab741b8466862": "{0} プロパティーには無効な節 {1} があります",
"67c2bf43b5281ab929617423ea8a6f3e": "コネクター {0} では {{replaceById}} 操作はサポートされません。 これは LoopBack のバグではありません。 コネクターの作成者に (なるべく GitHub Issue を通して) 問い合わせてください。",
"6c3234937d69763fc7f6bcafccc59bbc": "{{Model::deleteById}} には {{id}} 引数が必要です",
"6eb6fd4fbd73394000bc25f5776fd20c": "{{Model::exists}} には {{id}} 引数が必要です",
"7bbbdece4eea90e42aa5c0bce295e503": "{{Model::findById}} には {{id}} 引数が必要です",
"9da58ab4555ac50f4999883de6470d7d": "{0} 演算子には無効な節 {1} があります",
"a829dee089c912e68c18920ba015400c": "警告: {{'loaded'}} 操作フックでモデル {2} の {{id}} プロパティーを {0} から {1} に変更することはできません",
"a984a076c59e451948b2bcf7a393d860": "警告: {{'before save'}} 操作フックでモデル {2} の {{id}} プロパティーを {0} から {1} に変更することはできません",
"baf2c8b0c5a574b8a894e9b6304fece1": "where 節 {0} が {{object}} ではありません",
"db03083e9a768388fdbee865249ac67a": "{{updateOrCreate()}} での妥当性検査エラーを無視します:",
"e4434de4bb8f5a3cd1d416e4d80d7e0b": "\"{0}\" {{id}} \"{1}\" が不明です。",
"e54f118a75e15e132f16b985274eb46d": "照会フィルター {0} が {{object}} ではありません",
"f6e8c96c93b9c7687d6c172b3695e898": "{{id}} プロパティー ({0}) を {1} から {2} に更新できません",
"0be2d39d225b1d8b2a0f92ad5c65c9ac": "{{polymorphic}} {0}: {1} にモデルが指定されていません",
"2f4af31c144bbfab1bbf479866acd820": "\n警告: {{LoopBack}} コネクター \"{0}\" は次のいずれのモジュールとしてもインストールされていません:\n\n {1}\n\n修正するには、以下を実行します。\n\n {{npm install {2} --save}}\n",
"6111399276924ffa3bc9a410cdfcb2e5": "{{id}} 名 {0} がありません",
"75518263527c423b8a12f908a2730b0f": "{{className}} は必須です",
"791ab3031a73ede03f7d6299a85e8289": "接続は {0} ミリ秒後にタイムアウトになります",
"7b277018e43d41bc445731092b91547d": "未接続",
"a2487abefef4259c2131d96cdb8543b1": "接続失敗: {0}\n次の要求で再試行されます。",
"b15b20280211ad258d92947f05b6e4a5": "コネクターが初期化されていません。",
"e0e9504e137a3c3339144b51ed76fef2": "コネクターが正しく定義されていません: dataSource の `{{connector}}` メンバーを作成する必要があります",
"ec42dca074f1818c447f7ad16e2d01af": "{0} は付加されたコネクターによって提供されません",
"ba0fd8106eb54de4d003a844206431fd": "モデル・フック \"{0}\" は非推奨です。代わりに操作フックを使用してください。 {{http://docs.strongloop.com/display/LB/Operation+hooks}}",
"280f4550f90e133118955ec6f6f72830": "判別プログラム・タイプ {0} が指定されていますが、このような名前のモデルは存在しません",
"38dbf42c29a4645238cc3d632e88ebc9": "{{Relation.modelTo}} は関係 {0} に定義されておらず、{{polymorphic}} ではありません",
"eb56c2b0c30cf006e2df00a549ec9c2c": "関係 \"{0}\" は {1} モデルに定義されていません",
"938401ea4ce48159efa9be1d4a5e8bab": "項目は配列でなければなりません: {0}",
"f41bd91dc0f000a79c0bf842f1b7fdf9": "JSON ストリングからリストを作成できませんでした: {0}",
"514985b2327f061ffb1c932f6b909979": "モデル {0} が定義されていません。",
"cc09e361de7b0f3dc1687515c5713885": "ドットが含まれたプロパティー名はサポートされていません。 モデル: {0}、プロパティー: {1}",
"da02dd6c53d4148320eeb31718a7aebe": "プロパティー {0} のタイプが無効です",
"ddf0aa14803f1c84f4a97f3803f7471c": "クラス名は必須です",
"1853f9414bc61ed196952f5c81e41802": "ドットが含まれたプロパティー名はサポートされていません。 モデル: {0}、動的プロパティー: {1}",
"bdb11cc1c780c9ccac33c316cfdc9d82": "プロパティー {0}.{1} にタイプが定義されていません",
"cd930369e86cdd222f7bd117c6f9fa94": "不明なデフォルト値プロバイダー {0}",
"0c0b867aca0973ba26e887d3337cc4ec": "{{Polymorphic}} モデルが見つかりません: `{0}` が設定されていません",
"2f062cbecdf24245731bddc77714c814": "{1} の \"{0}\" 関係が見つかりませんでした",
"3cde8cc9bca22c67278b202ab0720106": "{1} に関する ID {0} のインスタンスが見つかりません",
"6502a117987610380b9068ef98b1b0ee": "({1}.{2}、{3}.{4}) に関して {0} でレコードが見つかりません",
"6fcc2ff0db7a4f490f5e0ce9e24691f3": "{{HasOne}} 関係では {0} のインスタンスを複数作成することはできません",
"7e9530c0399289be0ee601a604be71ff": "{{BelongsTo}} 関係 {0} が空です",
"7faa840eb6ce11250a141deb42a6c489": "不明な関係 {{scope}}: {0}",
"89afd3a9249f5a8d3edda07d84ca049d": "{{Polymorphic}} モデルが見つかりません: `{0}`",
"a004f310d315e592843776fab964eaeb": "{{Polymorphic}} 関係にはスルー・モデルが必要です",
"a25e41a39c60c4702e55d0c3936576a1": "キーの不一致: {0}.{1}: {2}、{3}.{4}: {5}",
"a327355560d495454fba2c1aad6bdf09": "不明なスコープ・メソッド: {0}",
"e08ab0e1ab55f26c357061447b635905": "({1}.{2}、{3}.{4}) に関して {0} で関係が見つかりません",
"e55937649d8d7a11706b8cec22d02eae": "{{HasOne}} 関係 {0} が空です",
"2c4904377a87fdab502118719cc0d266": "{{Transaction}} はサポートされていません",
"ecb7aa804bf54c682999d20d6436104c": "{{transaction}} がアクティブではありません: {0}",
"89bf6d92731fe7bd2146ce8d0bec205c": "引数が無効です。ストリング、{{regex}} リテラル、または {{RegExp}} オブジェクトでなければなりません",
"8c5ab01638c1ac1d58168c6346a8481a": "無効な {{regex}} フラグ: {0}",
"a6c18a7f4390cd3d59a2a7a047ae2aab": "\"{{npm install loopback-datasource-juggler}} {0}\" コマンドを実行します ",
"b138294f132edfe1eb2a8211150c7238": "照会内に予期しない `undefined` があります",
"8a39126103a157f501affa070367a1b0": "{0} インスタンスは無効です。 詳細: {1}。"
}

74
intl/ko/messages.json Normal file
View File

@ -0,0 +1,74 @@
{
"0bd753a8944ad0af85a939bb25273887": "알 수 없는 키 {0}을(를) 만료할 수 없음",
"bec226891a505828bfc76c5cfd73b336": "알 수 없는 키 {0}에 대한 TTL을 가져올 수 없음",
"4c78325cedbb826db3a05bf5df0e8546": "바꾸는 경우 {{id}}을(를) 제공해야 합니다!",
"a0cf0e09c26df14283223e84e6a10f00": "속성을 업데이트할 수 없습니다. {{id}} {0}의 {{Object}}이(가) 없습니다!",
"e54d944c2a2c85a23caa86027ae307cf": "이 데이터 소스에 첨부되지 않은 모델은 마이그레이션할 수 없음: {0}",
"e6161ae8459c79d810e2aa9d21282a39": "속성을 업데이트하는 경우 {{id}}을(를) 제공해야 합니다!",
"fca4d12faff1035d9d0438d73432571b": "{0}.{1}의 중복 항목",
"09483e03b91c8bd58732a74b3ef1ec13": "올바르지 않은 날짜: {0}",
"0b16d3ffc42f91b4b9a4b3b50c41c838": "{0} 순서가 올바르지 않음",
"0e88a84c6bc2638fbcc5d3ea95181f99": "알 수 없는 특성: {0}",
"21095484501dbff31af6556fa6039182": "{{offset/skip}} 매개변수 {0}이(가) 올바르지 않음",
"416dfbb7b823f51c9f3800be81060b41": "{1}에 대해 {{id}} {0}의 인스턴스를 찾을 수 없음",
"4e31b1edd10dadb724d83387de0b5062": "{{limit}} 매개변수 {0}이(가) 올바르지 않음",
"5ec8efeb715a2c34b440f2d76e2cf87d": " {0}",
"62a2d80c405b7fec5f547c448ab1b6ff": "{{order}} {0}에 올바르지 않은 방향이 있음",
"63f503acabdc4c7588aab741b8466862": "{0} 특성에 올바르지 않은 절 {1}이(가) 있음",
"67c2bf43b5281ab929617423ea8a6f3e": "커넥터 {0}에서 {{replaceById}} 오퍼레이션을 지원하지 않습니다. 이는 LoopBack의 버그가 아닙니다. 커넥터 작성자에게 문의하십시오. GitHub 발행을 사용하는 것이 좋습니다. ",
"6c3234937d69763fc7f6bcafccc59bbc": "{{Model::deleteById}}에 {{id}} 인수가 필요함",
"6eb6fd4fbd73394000bc25f5776fd20c": "{{Model::exists}}에 {{id}} 인수가 필요함",
"7bbbdece4eea90e42aa5c0bce295e503": "{{Model::findById}}에 {{id}} 인수가 필요함",
"9da58ab4555ac50f4999883de6470d7d": "{0} 연산자에 올바르지 않은 절 {1}이(가) 있음",
"a829dee089c912e68c18920ba015400c": "경고: {{'loaded'}} 오퍼레이션 후크에서 모델:{2}에 대해 {{id}} 특성을 {0}에서 {1}(으)로 변경할 수 없습니다. ",
"a984a076c59e451948b2bcf7a393d860": "경고: {{'before save'}} 오퍼레이션 후크에서 모델:{2}에 대해 {{id}} 특성을 {0}에서 {1}(으)로 변경할 수 없습니다. ",
"baf2c8b0c5a574b8a894e9b6304fece1": "where 절 {0}이(가) {{object}}이(가) 아님",
"db03083e9a768388fdbee865249ac67a": "{{updateOrCreate()}}에서 유효성 검증 오류 무시:",
"e4434de4bb8f5a3cd1d416e4d80d7e0b": "알 수 없는 \"{0}\" {{id}} \"{1}\".",
"e54f118a75e15e132f16b985274eb46d": "조회 필터 {0}이(가) {{object}}가 아님",
"f6e8c96c93b9c7687d6c172b3695e898": "{{id}} 특성({0})을 {1}에서 {2}(으)로 업데이트할 수 없음",
"0be2d39d225b1d8b2a0f92ad5c65c9ac": "{{polymorphic}} {0}에 지정된 모델이 없음: {1}",
"2f4af31c144bbfab1bbf479866acd820": "\n경고: {{LoopBack}} 커넥터 \"{0}\"이(가) 다음 모듈로 설치되어 있지 않음:\n\n {1}\n\n이를 수정하려면 다음을 실행하십시오. \n\n {{npm install {2} --save}}\n",
"6111399276924ffa3bc9a410cdfcb2e5": "{{id}} 이름 {0}이(가) 아님",
"75518263527c423b8a12f908a2730b0f": "{{className}}은(는) 필수입니다.",
"791ab3031a73ede03f7d6299a85e8289": "{0}밀리초 후 연결 제한시간 초과",
"7b277018e43d41bc445731092b91547d": "연결되지 않음",
"a2487abefef4259c2131d96cdb8543b1": "연결 실패: {0}\n다음 요청에서 재시도됩니다.",
"b15b20280211ad258d92947f05b6e4a5": "커넥터가 초기화되지 않았습니다.",
"e0e9504e137a3c3339144b51ed76fef2": "커넥터가 제대로 정의되지 않음: 데이터 소스의 `{{connector}}` 멤버를 작성해야 합니다. ",
"ec42dca074f1818c447f7ad16e2d01af": "첨부된 커넥터에서 {0}을(를) 제공하지 않음",
"ba0fd8106eb54de4d003a844206431fd": "모델 후크 \"{0}\"이(가) 더 이상 사용되지 않습니다. 오퍼레이션 후크가 대신 사용됩니다. {{http://docs.strongloop.com/display/LB/Operation+hooks}}",
"280f4550f90e133118955ec6f6f72830": "식별자 유형 {0}이(가) 지정되었지만 해당 이름의 모델이 없습니다.",
"38dbf42c29a4645238cc3d632e88ebc9": "관계 {0}에 대해 {{Relation.modelTo}}이(가) 정의되지 않았으며 {{polymorphic}}이(가) 아닙니다. ",
"eb56c2b0c30cf006e2df00a549ec9c2c": "{1} 모델에 대해 관계 \"{0}\"이(가) 정의되지 않음",
"938401ea4ce48159efa9be1d4a5e8bab": "항목이 배열이어야 함: {0}",
"f41bd91dc0f000a79c0bf842f1b7fdf9": "JSON 문자열에서 목록을 작성할 수 없음: {0}",
"514985b2327f061ffb1c932f6b909979": "{0} 모델이 정의되지 않았습니다.",
"cc09e361de7b0f3dc1687515c5713885": "점이 포함된 특성 이름은 지원되지 않습니다. 모델: {0}, 특성: {1}",
"da02dd6c53d4148320eeb31718a7aebe": "특성 {0}에 대한 올바르지 않은 유형",
"ddf0aa14803f1c84f4a97f3803f7471c": "클래스 이름 필수",
"1853f9414bc61ed196952f5c81e41802": "점이 포함된 특성 이름은 지원되지 않습니다. 모델: {0}, 동적 특성: {1}",
"bdb11cc1c780c9ccac33c316cfdc9d82": "특성 {0}.{1}에 유형이 정의되지 않음",
"cd930369e86cdd222f7bd117c6f9fa94": "알 수 없는 기본값 제공자 {0}",
"0c0b867aca0973ba26e887d3337cc4ec": "{{Polymorphic}} 모델을 찾을 수 없음: `{0}`이(가) 설정되지 않음",
"2f062cbecdf24245731bddc77714c814": "{1}에 대한 \"{0}\" 관계를 찾을 수 없음",
"3cde8cc9bca22c67278b202ab0720106": "{1}에 대해 ID {0}의 인스턴스를 찾을 수 없음",
"6502a117987610380b9068ef98b1b0ee": "{0}에서 ({1}.{2} ,{3}.{4})에 대한 레코드를 찾을 수 없음",
"6fcc2ff0db7a4f490f5e0ce9e24691f3": "{{HasOne}} 관계에서 둘 이상의 {0} 인스턴스를 작성할 수 없음",
"7e9530c0399289be0ee601a604be71ff": "{{BelongsTo}} 관계 {0}이(가) 비어 있음",
"7faa840eb6ce11250a141deb42a6c489": "알 수 없는 관계 {{scope}}: {0}",
"89afd3a9249f5a8d3edda07d84ca049d": "{{Polymorphic}} 모델을 찾을 수 없음: `{0}`",
"a004f310d315e592843776fab964eaeb": "{{Polymorphic}} 관계에는 through 모델이 필요함",
"a25e41a39c60c4702e55d0c3936576a1": "키 불일치: {0}.{1}: {2}, {3}.{4}: {5}",
"a327355560d495454fba2c1aad6bdf09": "알 수 없는 범위 메소드: {0}",
"e08ab0e1ab55f26c357061447b635905": "{0}에서 ({1}.{2} ,{3}.{4})에 대한 관계를 찾을 수 없음",
"e55937649d8d7a11706b8cec22d02eae": "{{HasOne}} 관계 {0}이(가) 비어 있음",
"2c4904377a87fdab502118719cc0d266": "{{Transaction}}이(가) 지원되지 않음",
"ecb7aa804bf54c682999d20d6436104c": "{{transaction}}이(가) 활성 상태가 아님: {0}",
"89bf6d92731fe7bd2146ce8d0bec205c": "올바르지 않은 인수가 문자열, {{regex}} 리터럴 또는 {{RegExp}} 오브젝트여야 합니다.",
"8c5ab01638c1ac1d58168c6346a8481a": "올바르지 않은 {{regex}} 플래그: {0}",
"a6c18a7f4390cd3d59a2a7a047ae2aab": "\"{{npm install loopback-datasource-juggler}} {0}\" 명령 실행",
"b138294f132edfe1eb2a8211150c7238": "조회에서 예상치 못한 `undefined` 항목",
"8a39126103a157f501affa070367a1b0": "{0} 인스턴스가 올바르지 않습니다. 세부사항: {1}."
}

74
intl/nl/messages.json Normal file
View File

@ -0,0 +1,74 @@
{
"0bd753a8944ad0af85a939bb25273887": "Onbekende sleutel {0} kan niet vervallen",
"bec226891a505828bfc76c5cfd73b336": "TTL voor onbekende sleutel {0} kan niet worden opgehaald",
"4c78325cedbb826db3a05bf5df0e8546": "U moet een {{id}} opgeven bij een vervanging.",
"a0cf0e09c26df14283223e84e6a10f00": "De kenmerken kunnen niet worden bijgewerkt. {{Object}} met {{id}} {0} bestaat niet!",
"e54d944c2a2c85a23caa86027ae307cf": "Kan geen modellen migreren die niet zijn gekoppeld aan deze gegevensbron: {0}",
"e6161ae8459c79d810e2aa9d21282a39": "U moet een {{id}} opgeven bij het bijwerken van kenmerken.",
"fca4d12faff1035d9d0438d73432571b": "Dubbel item voor {0}.{1}",
"09483e03b91c8bd58732a74b3ef1ec13": "Ongeldige datum: {0}",
"0b16d3ffc42f91b4b9a4b3b50c41c838": "De volgorde {0} is niet geldig",
"0e88a84c6bc2638fbcc5d3ea95181f99": "Onbekende eigenschap: {0}",
"21095484501dbff31af6556fa6039182": "De {{offset/skip}}-parameter {0} is niet geldig",
"416dfbb7b823f51c9f3800be81060b41": "Geen instance met {{id}} {0} gevonden voor {1}",
"4e31b1edd10dadb724d83387de0b5062": "De {{limit}}-parameter {0} is niet geldig",
"5ec8efeb715a2c34b440f2d76e2cf87d": " {0}",
"62a2d80c405b7fec5f547c448ab1b6ff": "De {{order}} {0} heeft een ongeldige richting",
"63f503acabdc4c7588aab741b8466862": "De eigenschap {0} heeft een ongeldige clausule {1}",
"67c2bf43b5281ab929617423ea8a6f3e": "De connector {0} biedt geen ondersteuning voor de bewerking {{replaceById}}. Dit is geen programmafout in LoopBack. Neem contact op met de auteurs van de connector, bij voorkeur via GitHub-problemen.",
"6c3234937d69763fc7f6bcafccc59bbc": "Voor {{Model::deleteById}} is het argument {{id}} vereist",
"6eb6fd4fbd73394000bc25f5776fd20c": "Voor {{Model::exists}} is het argument {{id}} vereist",
"7bbbdece4eea90e42aa5c0bce295e503": "Voor {{Model::findById}} is het argument {{id}} vereist",
"9da58ab4555ac50f4999883de6470d7d": "De operator {0} heeft ongeldige clausules {1}",
"a829dee089c912e68c18920ba015400c": "Waarschuwing: Eigenschap {{id}} kan niet worden gewijzigd van {0} in {1} voor model:{2} in operation hook {{'loaded'}}",
"a984a076c59e451948b2bcf7a393d860": "Waarschuwing: Eigenschap {{id}} kan niet worden gewijzigd van {0} in {1} voor model:{2} in operation hook {{'before save'}}",
"baf2c8b0c5a574b8a894e9b6304fece1": "De WHERE-clausule {0} is geen {{object}}",
"db03083e9a768388fdbee865249ac67a": "Validatiefouten in {{updateOrCreate()}} worden genegeerd:",
"e4434de4bb8f5a3cd1d416e4d80d7e0b": "Onbekend \"{0}\" {{id}} \"{1}\".",
"e54f118a75e15e132f16b985274eb46d": "Het queryfilter {0} is geen {{object}}",
"f6e8c96c93b9c7687d6c172b3695e898": "{{id}}-eigenschap ({0}) kan niet worden bijgewerkt van {1} in {2}",
"0be2d39d225b1d8b2a0f92ad5c65c9ac": "Geen model opgegeven voor {{polymorphic}} {0}: {1}",
"2f4af31c144bbfab1bbf479866acd820": "\nWaarschuwing: {{LoopBack}}-connector \"{0}\" is niet geïnstalleerd als een van de volgende modules:\n\n {1}\n\nU lost dit op door het uitvoeren van:\n\n {{npm install {2} --save}}\n",
"6111399276924ffa3bc9a410cdfcb2e5": "Geen {{id}}-naam {0}",
"75518263527c423b8a12f908a2730b0f": "{{className}} is verplicht",
"791ab3031a73ede03f7d6299a85e8289": "Timeout voor het maken van verbinding na {0} ms",
"7b277018e43d41bc445731092b91547d": "Geen verbinding",
"a2487abefef4259c2131d96cdb8543b1": "Verbinding mislukt: {0}\nVerbindingspoging wordt herhaald bij volgende opdracht.",
"b15b20280211ad258d92947f05b6e4a5": "De connector is niet geïnitialiseerd.",
"e0e9504e137a3c3339144b51ed76fef2": "Connector is niet juist gedefinieerd: moet '{{connector}}'-lid van gegevensbron maken",
"ec42dca074f1818c447f7ad16e2d01af": "{0} is niet opgegeven door gekoppelde connector",
"ba0fd8106eb54de4d003a844206431fd": "Model hook \"{0}\" is gedeprecieerd; gebruik in plaats daarvan operation hooks. {{http://docs.strongloop.com/display/LB/Operation+hooks}}",
"280f4550f90e133118955ec6f6f72830": "Discriminatortype {0} opgegeven, maar er bestaat geen model met deze naam",
"38dbf42c29a4645238cc3d632e88ebc9": "{{Relation.modelTo}} is niet gedefinieerd voor relatie {0} en is geen {{polymorphic}}",
"eb56c2b0c30cf006e2df00a549ec9c2c": "Relatie \"{0}\" is niet gedefinieerd voor model {1}",
"938401ea4ce48159efa9be1d4a5e8bab": "Items moeten een array zijn: {0}",
"f41bd91dc0f000a79c0bf842f1b7fdf9": "kan geen lijst maken op basis van JSON-reeks: {0}",
"514985b2327f061ffb1c932f6b909979": "Model {0} is niet gedefinieerd.",
"cc09e361de7b0f3dc1687515c5713885": "Eigenschapnamen met een punt worden niet ondersteund. Model: {0}, eigenschap: {1}",
"da02dd6c53d4148320eeb31718a7aebe": "Ongeldig type voor eigenschap {0}",
"ddf0aa14803f1c84f4a97f3803f7471c": "Klassennaam vereist",
"1853f9414bc61ed196952f5c81e41802": "Eigenschapnamen met een punt worden niet ondersteund. Model: {0}, dynamische eigenschap: {1}",
"bdb11cc1c780c9ccac33c316cfdc9d82": "Type niet gedefinieerd voor eigenschap {0}.{1}",
"cd930369e86cdd222f7bd117c6f9fa94": "Onbekende standaardwaarde voor provider {0}",
"0c0b867aca0973ba26e887d3337cc4ec": "Model {{Polymorphic}} is niet gevonden: '{0}' niet ingesteld",
"2f062cbecdf24245731bddc77714c814": "Kan \"{0}\"-relatie voor {1} niet vinden",
"3cde8cc9bca22c67278b202ab0720106": "Geen instance met ID {0} gevonden voor {1}",
"6502a117987610380b9068ef98b1b0ee": "Geen record gevonden in {0} voor ({1}.{2} ,{3}.{4})",
"6fcc2ff0db7a4f490f5e0ce9e24691f3": "{{HasOne}}-relatie kan niet resulteren in meer dan één instance van {0}",
"7e9530c0399289be0ee601a604be71ff": "{{BelongsTo}}-relatie {0} is leeg",
"7faa840eb6ce11250a141deb42a6c489": "Onbekende relatie {{scope}}: {0}",
"89afd3a9249f5a8d3edda07d84ca049d": "{{Polymorphic}}-model is niet gevonden: '{0}'",
"a004f310d315e592843776fab964eaeb": "{{Polymorphic}}-relaties hebben een doorvoermodel nodig",
"a25e41a39c60c4702e55d0c3936576a1": "Niet-overeenkomende sleutels: {0}.{1}: {2}, {3}.{4}: {5}",
"a327355560d495454fba2c1aad6bdf09": "Onbekende methode voor bereik: {0}",
"e08ab0e1ab55f26c357061447b635905": "Geen relatie gevonden in {0} voor ({1}.{2},{3}.{4})",
"e55937649d8d7a11706b8cec22d02eae": "{{HasOne}}-relatie {0} is leeg",
"2c4904377a87fdab502118719cc0d266": "{{Transaction}} wordt niet ondersteund",
"ecb7aa804bf54c682999d20d6436104c": "De {{transaction}} is niet actief: {0}",
"89bf6d92731fe7bd2146ce8d0bec205c": "Ongeldig argument, moet een tekenreeks, {{regex}}-literaal of {{RegExp}}-object zijn",
"8c5ab01638c1ac1d58168c6346a8481a": "Ongeldige {{regex}}-vlaggen: {0}",
"a6c18a7f4390cd3d59a2a7a047ae2aab": "Voer de opdracht \"{{npm install loopback-datasource-juggler}} {0}\" uit",
"b138294f132edfe1eb2a8211150c7238": "Onverwacht item 'undefined' in query",
"8a39126103a157f501affa070367a1b0": "De instance {0} is niet geldig. Details: {1}."
}

74
intl/pt/messages.json Normal file
View File

@ -0,0 +1,74 @@
{
"0bd753a8944ad0af85a939bb25273887": "Não é possível expirar chave desconhecida {0}",
"bec226891a505828bfc76c5cfd73b336": "Não é possível obter TTL para chave desconhecida {0}",
"4c78325cedbb826db3a05bf5df0e8546": "Deve-se fornecer um {{id}} ao substituir!",
"a0cf0e09c26df14283223e84e6a10f00": "Não foi possível atualizar atributos. {{Object}} com {{id}} {0} não existe!",
"e54d944c2a2c85a23caa86027ae307cf": "Não é possível migrar modelos não conectados a esta origem de dados: {0}",
"e6161ae8459c79d810e2aa9d21282a39": "Deve-se fornecer um {{id}} ao atualizar atributos!",
"fca4d12faff1035d9d0438d73432571b": "Entrada duplicada para {0}.{1}",
"09483e03b91c8bd58732a74b3ef1ec13": "Data inválida: {0}",
"0b16d3ffc42f91b4b9a4b3b50c41c838": "A ordem {0} não é válida",
"0e88a84c6bc2638fbcc5d3ea95181f99": "Propriedade desconhecida: {0}",
"21095484501dbff31af6556fa6039182": "O parâmetro {{offset/skip}} {0} não é válido",
"416dfbb7b823f51c9f3800be81060b41": "Nenhuma instância com {{id}} {0} localizada para {1}",
"4e31b1edd10dadb724d83387de0b5062": "O parâmetro {0} de {{limit}} não é válido",
"5ec8efeb715a2c34b440f2d76e2cf87d": " {0}",
"62a2d80c405b7fec5f547c448ab1b6ff": "A {{order}} {0} possui direção inválida",
"63f503acabdc4c7588aab741b8466862": "A propriedade {0} possui cláusula inválida {1}",
"67c2bf43b5281ab929617423ea8a6f3e": "O conector {0} não suporta operação {{replaceById}}. Este não é um erro no LoopBack. Entre em contato com os autores do conector, de preferência via problemas do GitHub.",
"6c3234937d69763fc7f6bcafccc59bbc": "{{Model::deleteById}} requer o argumento {{id}}",
"6eb6fd4fbd73394000bc25f5776fd20c": "{{Model::exists}} requer o argumento {{id}}",
"7bbbdece4eea90e42aa5c0bce295e503": "{{Model::findById}} requer o argumento {{id}}",
"9da58ab4555ac50f4999883de6470d7d": "O operador {0} possui cláusulas {1} inválidas",
"a829dee089c912e68c18920ba015400c": "AVISO: a propriedade {{id}} não pode ser mudada de {0} para {1} para o modelo:{2} no gancho de operação {{'loaded'}}",
"a984a076c59e451948b2bcf7a393d860": "AVISO: a propriedade {{id}} não pode ser mudada de {0} para {1} para o modelo:{2} no gancho de operação {{'before save'}}",
"baf2c8b0c5a574b8a894e9b6304fece1": "A cláusula where {0} não é um {{object}}",
"db03083e9a768388fdbee865249ac67a": "Ignorando erros de validação em {{updateOrCreate()}}:",
"e4434de4bb8f5a3cd1d416e4d80d7e0b": "\"{0}\" {{id}} \"{1}\" desconhecido.",
"e54f118a75e15e132f16b985274eb46d": "O filtro de consulta {0} não é um {{object}}",
"f6e8c96c93b9c7687d6c172b3695e898": "A propriedade de {{id}} ({0}) não pode ser atualizada de {1} para {2}",
"0be2d39d225b1d8b2a0f92ad5c65c9ac": "Nenhum modelo especificado para {{polymorphic}} {0}: {1}",
"2f4af31c144bbfab1bbf479866acd820": "\nAVISO: conector {{LoopBack}} \"{0}\" não foi instalado como qualquer um dos módulos a seguir:\n\n {1}\n\nPara corrigir, execute:\n\n {{npm install {2} --save}}\n",
"6111399276924ffa3bc9a410cdfcb2e5": "Nenhum nome de {{id}} {0}",
"75518263527c423b8a12f908a2730b0f": "{{className}} é obrigatório",
"791ab3031a73ede03f7d6299a85e8289": "Tempo limite na conexão após {0} ms",
"7b277018e43d41bc445731092b91547d": "Não está conectado",
"a2487abefef4259c2131d96cdb8543b1": "Falha de conexão: {0}\nEla será tentada novamente para a próxima solicitação.",
"b15b20280211ad258d92947f05b6e4a5": "O conector não foi inicializado.",
"e0e9504e137a3c3339144b51ed76fef2": "O conector não foi definido corretamente: ele deve criar um membro de `{{connector}}` igual a dataSource",
"ec42dca074f1818c447f7ad16e2d01af": "{0} não é fornecido pelo conector conectado",
"ba0fd8106eb54de4d003a844206431fd": "O gancho de modelo \"{0}\" está descontinuado, use ganchos de Operação no lugar. {{http://docs.strongloop.com/display/LB/Operation+hooks}}",
"280f4550f90e133118955ec6f6f72830": "Tipo de discriminador {0} especificado, mas não existe nenhum modelo com esse nome",
"38dbf42c29a4645238cc3d632e88ebc9": "{{Relation.modelTo}} não foi definido para a relação {0} e não é {{polymorphic}}",
"eb56c2b0c30cf006e2df00a549ec9c2c": "A relação \"{0}\" não foi definida para o modelo {1}",
"938401ea4ce48159efa9be1d4a5e8bab": "Itens devem ser uma matriz: {0}",
"f41bd91dc0f000a79c0bf842f1b7fdf9": "não foi possível criar Lista a partir da sequência JSON: {0}",
"514985b2327f061ffb1c932f6b909979": "O modelo {0} não está definido.",
"cc09e361de7b0f3dc1687515c5713885": "Os nomes da propriedade contendo um ponto não são suportados. Modelo: {0}, propriedade: {1}",
"da02dd6c53d4148320eeb31718a7aebe": "Tipo inválido para a propriedade {0}",
"ddf0aa14803f1c84f4a97f3803f7471c": "Nome de classe necessário",
"1853f9414bc61ed196952f5c81e41802": "Os nomes da propriedade contendo um ponto não são suportados. Modelo: {0}, propriedade dinâmica: {1}",
"bdb11cc1c780c9ccac33c316cfdc9d82": "Tipo não definido para a propriedade {0}.{1}",
"cd930369e86cdd222f7bd117c6f9fa94": "Provedor do valor padrão {0} desconhecido",
"0c0b867aca0973ba26e887d3337cc4ec": "Modelo de {{Polymorphic}} não localizado: `{0}` não configurado",
"2f062cbecdf24245731bddc77714c814": "Não foi possível localizar relação de \"{0}\" para {1}",
"3cde8cc9bca22c67278b202ab0720106": "Nenhuma instância com ID {0} localizada para {1}",
"6502a117987610380b9068ef98b1b0ee": "Nenhum registro localizado em {0} para ({1}.{2} ,{3}.{4})",
"6fcc2ff0db7a4f490f5e0ce9e24691f3": "A relação {{HasOne}} não pode criar mais de uma instância de {0}",
"7e9530c0399289be0ee601a604be71ff": "Relação {{BelongsTo}} {0} está vazia",
"7faa840eb6ce11250a141deb42a6c489": "{{scope}} da relação desconhecido: {0}",
"89afd3a9249f5a8d3edda07d84ca049d": "Modelo de {{Polymorphic}} não localizado: `{0}`",
"a004f310d315e592843776fab964eaeb": "Relações de {{Polymorphic}} precisam de um modelo completo",
"a25e41a39c60c4702e55d0c3936576a1": "Incompatibilidade de chave: {0}.{1}: {2}, {3}.{4}: {5}",
"a327355560d495454fba2c1aad6bdf09": "Método de escopo desconhecido: {0}",
"e08ab0e1ab55f26c357061447b635905": "Nenhuma relação localizada em {0} para ({1}.{2},{3}.{4})",
"e55937649d8d7a11706b8cec22d02eae": "Relação {{HasOne}} {0} está vazia",
"2c4904377a87fdab502118719cc0d266": "{{Transaction}} não é suportada",
"ecb7aa804bf54c682999d20d6436104c": "A {{transaction}} não está ativa: {0}",
"89bf6d92731fe7bd2146ce8d0bec205c": "Argumento inválido, deve ser uma sequência, literal {{regex}} ou objeto {{RegExp}}",
"8c5ab01638c1ac1d58168c6346a8481a": "Sinalizações de {{regex}} inválidas: {0}",
"a6c18a7f4390cd3d59a2a7a047ae2aab": "Execute o comando \"{{npm install loopback-datasource-juggler}} {0}\" ",
"b138294f132edfe1eb2a8211150c7238": "`Indefinido` inesperado na consulta",
"8a39126103a157f501affa070367a1b0": "A instância de {0} não é válida. Detalhes: {1}."
}

74
intl/tr/messages.json Normal file
View File

@ -0,0 +1,74 @@
{
"0bd753a8944ad0af85a939bb25273887": "Bilinmeyen {0} anahtarı süre bitimine uğratılamaz",
"bec226891a505828bfc76c5cfd73b336": "Bilinmeyen {0} anahtarı için TTL alınamıyor",
"4c78325cedbb826db3a05bf5df0e8546": "Değiştirirken bir {{id}} belirtmelisiniz!",
"a0cf0e09c26df14283223e84e6a10f00": "Öznitelikler güncellenemedi. Tanıtıcısı {{id}} olan {0} {{Object}} nesnesi yok!",
"e54d944c2a2c85a23caa86027ae307cf": "Bu veri kaynağına eklenmemiş modeller geçirilemez: {0}",
"e6161ae8459c79d810e2aa9d21282a39": "Öznitelikleri güncellerken bir {{id}} belirtmelisiniz!",
"fca4d12faff1035d9d0438d73432571b": "Bu öğeyle ilgili yinelenen giriş: {0}.{1}",
"09483e03b91c8bd58732a74b3ef1ec13": "Geçersiz tarih: {0}",
"0b16d3ffc42f91b4b9a4b3b50c41c838": "{0} sırası geçerli değil",
"0e88a84c6bc2638fbcc5d3ea95181f99": "Bilinmeyen özellik: {0}",
"21095484501dbff31af6556fa6039182": "{{offset/skip}} parametresi {0} geçerli değil",
"416dfbb7b823f51c9f3800be81060b41": "{1} için {{id}} {0} tanıtıcılı bir eşgörünüm bulunamadı",
"4e31b1edd10dadb724d83387de0b5062": "{{limit}} parametresi {0} geçerli değil",
"5ec8efeb715a2c34b440f2d76e2cf87d": " {0}",
"62a2d80c405b7fec5f547c448ab1b6ff": "{{order}} {0} sırasının yönü geçersiz",
"63f503acabdc4c7588aab741b8466862": "{0} özelliğinde geçersiz {1} yantümcesi var",
"67c2bf43b5281ab929617423ea8a6f3e": "{0} bağlayıcısı {{replaceById}} işlemini desteklemiyor. Bu LoopBack'deki bir hata değil. Lütfen, mümkünse GitHub sorunlarıyla birlikte, bağlayıcının yazarlarına başvurun.",
"6c3234937d69763fc7f6bcafccc59bbc": "{{Model::deleteById}}, {{id}} bağımsız değişkenini gerektirir",
"6eb6fd4fbd73394000bc25f5776fd20c": "{{Model::exists}}, {{id}} bağımsız değişkenini gerektirir",
"7bbbdece4eea90e42aa5c0bce295e503": "{{Model::findById}}, {{id}} bağımsız değişkenini gerektirir",
"9da58ab4555ac50f4999883de6470d7d": "{0} işlecinde geçersiz {1} yantümceleri var",
"a829dee089c912e68c18920ba015400c": "UYARI: {{id}} özelliği, {{'loaded'}} işlem çengelinde {2} modeli için {0} değerinden {1} değerine çevrilemiyor",
"a984a076c59e451948b2bcf7a393d860": "UYARI: {{id}} özelliği, {{'before save'}} işlem çengelinde {2} modeli için {0} değerinden {1} değerine çevrilemiyor",
"baf2c8b0c5a574b8a894e9b6304fece1": "Where yantümcesi {0} bir {{object}} değil",
"db03083e9a768388fdbee865249ac67a": "{{updateOrCreate()}} işlemindeki doğrulama hataları yoksayılıyor:",
"e4434de4bb8f5a3cd1d416e4d80d7e0b": "Bilinmeyen \"{0}\" {{id}} \"{1}\".",
"e54f118a75e15e132f16b985274eb46d": "Sorgu süzgeci {0} bir {{object}} değil",
"f6e8c96c93b9c7687d6c172b3695e898": "{{id}} özelliği ({0}), {1} değerinden {2} değerine güncellenemiyor",
"0be2d39d225b1d8b2a0f92ad5c65c9ac": "{{polymorphic}} {0} için model belirtilmedi: {1}",
"2f4af31c144bbfab1bbf479866acd820": "\nUYARI: {{LoopBack}} bağlayıcısı \"{0}\" şu modüllerden biri olarak kurulmadı:\n\n {1}\n\nDüzeltmek için şu komutu çalıştırın:\n\n {{npm install {2} --save}}\n",
"6111399276924ffa3bc9a410cdfcb2e5": "{{id}} adı {0} yok",
"75518263527c423b8a12f908a2730b0f": "{{className}} zorunludur",
"791ab3031a73ede03f7d6299a85e8289": "{0} milisaniyeden sonra bağlantıda zaman aşımı oluştu",
"7b277018e43d41bc445731092b91547d": "Bağlı değil",
"a2487abefef4259c2131d96cdb8543b1": "Bağlantı başarısız oldu: {0}\nSonraki istek için yeniden denenecek.",
"b15b20280211ad258d92947f05b6e4a5": "Bağlayıcı başlatılmamış.",
"e0e9504e137a3c3339144b51ed76fef2": "Bağlayıcı doğru tanımlanmadı: Veri kaynağının `{{connector}}` üyesini yaratmalıdır",
"ec42dca074f1818c447f7ad16e2d01af": "Eklenen bağlayıcı tarafından {0} sağlanmadı",
"ba0fd8106eb54de4d003a844206431fd": "\"{0}\" model çengeli kullanım dışı bırakıldı, onun yerine işlem çengellerini kullanın. {{http://docs.strongloop.com/display/LB/Operation+hooks}}",
"280f4550f90e133118955ec6f6f72830": "{0} ayrımsayıcı tipi belirtildi, ancak bu adı taşıyan bir model yok",
"38dbf42c29a4645238cc3d632e88ebc9": "{{Relation.modelTo}}, {0} ilişkisi için tanımlanmadı ve {{polymorphic}} değil",
"eb56c2b0c30cf006e2df00a549ec9c2c": "{1} modeli için \"{0}\" ilişkisi tanımlanmadı",
"938401ea4ce48159efa9be1d4a5e8bab": "Öğeler bir dizi olmalıdır: {0}",
"f41bd91dc0f000a79c0bf842f1b7fdf9": "JSON dizgisinden liste yaratılamadı: {0}",
"514985b2327f061ffb1c932f6b909979": "{0} modeli tanımlanmadı",
"cc09e361de7b0f3dc1687515c5713885": "Nokta içeren özellik adları desteklenmez. Model: {0}, özellik: {1}",
"da02dd6c53d4148320eeb31718a7aebe": "{0} özelliği için geçersiz tip",
"ddf0aa14803f1c84f4a97f3803f7471c": "Sınıf adı zorunludur",
"1853f9414bc61ed196952f5c81e41802": "Nokta içeren özellik adları desteklenmez. Model: {0}, dinamik özellik: {1}",
"bdb11cc1c780c9ccac33c316cfdc9d82": "Bu özellik için tip tanımlanmadı: {0}.{1}",
"cd930369e86cdd222f7bd117c6f9fa94": "Bilinmeyen varsayılan değer sağlayıcısı {0}",
"0c0b867aca0973ba26e887d3337cc4ec": "{{Polymorphic}} model bulunamadı: `{0}` ayarlı değil",
"2f062cbecdf24245731bddc77714c814": "{1} için \"{0}\" bulunamadı",
"3cde8cc9bca22c67278b202ab0720106": "{1} için {0} tanıtıcılı bir eşgörünüm bulunamadı",
"6502a117987610380b9068ef98b1b0ee": "{0} içinde şu öğe için kayıt bulunamadı: ({1}.{2} ,{3}.{4})",
"6fcc2ff0db7a4f490f5e0ce9e24691f3": "{{HasOne}} ilişkisi birden çok {0} eşgörünümü yaratamaz",
"7e9530c0399289be0ee601a604be71ff": "{{BelongsTo}} ilişkisi {0} boş",
"7faa840eb6ce11250a141deb42a6c489": "Bilinmeyen ilişki {{scope}}: {0}",
"89afd3a9249f5a8d3edda07d84ca049d": "{{Polymorphic}} model bulunamadı: `{0}`",
"a004f310d315e592843776fab964eaeb": "{{Polymorphic}} ilişkileri için aracı model gerekir",
"a25e41a39c60c4702e55d0c3936576a1": "Anahtar uyuşmazlığı: {0}.{1}: {2}, {3}.{4}: {5}",
"a327355560d495454fba2c1aad6bdf09": "Bilinmeyen kapsam yöntemi: {0}",
"e08ab0e1ab55f26c357061447b635905": "{0} içinde şu öğe için ilişki bulunamadı: ({1}.{2},{3}.{4})",
"e55937649d8d7a11706b8cec22d02eae": "{{HasOne}} ilişkisi {0} boş",
"2c4904377a87fdab502118719cc0d266": "{{Transaction}} desteklenmiyor",
"ecb7aa804bf54c682999d20d6436104c": "{{transaction}} etkin değil: {0}",
"89bf6d92731fe7bd2146ce8d0bec205c": "Bağımsız değişken geçersiz; bir dizgi, {{regex}} hazır bilgisi ya da {{RegExp}} nesnesi olmalıdır",
"8c5ab01638c1ac1d58168c6346a8481a": "Geçersiz {{regex}} işaretleri: {0}",
"a6c18a7f4390cd3d59a2a7a047ae2aab": "\"{{npm install loopback-datasource-juggler}} {0}\" komutunu çalıştırın ",
"b138294f132edfe1eb2a8211150c7238": "Sorguda beklenmeyen `undefined`",
"8a39126103a157f501affa070367a1b0": "{0} eşgörünümü geçerli değil. Ayrıntılar: {1}."
}

View File

@ -0,0 +1,74 @@
{
"0bd753a8944ad0af85a939bb25273887": "无法使未知键 {0} 到期",
"bec226891a505828bfc76c5cfd73b336": "无法获取未知键 {0} 的 TTL",
"4c78325cedbb826db3a05bf5df0e8546": "在替换时必须提供 {{id}}",
"a0cf0e09c26df14283223e84e6a10f00": "无法更新属性。包含 {{id}} {0} 的 {{Object}} 不存在!",
"e54d944c2a2c85a23caa86027ae307cf": "无法迁移未附加到此数据源的模型:{0}",
"e6161ae8459c79d810e2aa9d21282a39": "在更新属性时必须提供 {{id}}",
"fca4d12faff1035d9d0438d73432571b": "{0} 的重复条目。{1}",
"09483e03b91c8bd58732a74b3ef1ec13": "无效日期:{0}",
"0b16d3ffc42f91b4b9a4b3b50c41c838": "顺序 {0} 无效",
"0e88a84c6bc2638fbcc5d3ea95181f99": "未知的属性:{0}",
"21095484501dbff31af6556fa6039182": "{{offset/skip}} 参数 {0} 无效",
"416dfbb7b823f51c9f3800be81060b41": "对于 {1},找不到具有 {{id}} {0} 的实例",
"4e31b1edd10dadb724d83387de0b5062": "{{limit}} 参数 {0} 无效",
"5ec8efeb715a2c34b440f2d76e2cf87d": " {0}",
"62a2d80c405b7fec5f547c448ab1b6ff": "{{order}} {0} 具有无效的方向",
"63f503acabdc4c7588aab741b8466862": "{0} 属性具有无效的子句 {1}",
"67c2bf43b5281ab929617423ea8a6f3e": "连接器 {0} 不支持 {{replaceById}} 操作。这不是 LoopBack 中的错误。请联系连接器的作者,首选通过 GitHub 提交问题。",
"6c3234937d69763fc7f6bcafccc59bbc": "{{Model::deleteById}} 需要 {{id}} 自变量",
"6eb6fd4fbd73394000bc25f5776fd20c": "{{Model::exists}} 需要 {{id}} 自变量",
"7bbbdece4eea90e42aa5c0bce295e503": "{{Model::findById}} 需要 {{id}} 自变量",
"9da58ab4555ac50f4999883de6470d7d": "{0} 运算符具有无效的子句 {1}",
"a829dee089c912e68c18920ba015400c": "警告:对于以下模型,{{id}} 属性无法从 {0} 更改为 {1}{{'loaded'}} 操作挂钩中的 {2}",
"a984a076c59e451948b2bcf7a393d860": "警告:对于以下模型,{{id}} 属性无法从 {0} 更改为 {1}{{'before save'}} 操作挂钩中的 {2}",
"baf2c8b0c5a574b8a894e9b6304fece1": "Where 子句 {0} 不是 {{object}}",
"db03083e9a768388fdbee865249ac67a": "正在忽略 {{updateOrCreate()}} 中的验证错误:",
"e4434de4bb8f5a3cd1d416e4d80d7e0b": "未知的“{0}”{{id}}“{1}”。",
"e54f118a75e15e132f16b985274eb46d": "查询过滤器 {0} 不是 {{object}}",
"f6e8c96c93b9c7687d6c172b3695e898": "{{id}} 属性 ({0}) 无法从 {1} 更新为 {2}",
"0be2d39d225b1d8b2a0f92ad5c65c9ac": "未针对 {{polymorphic}} {0} 指定模型:{1}",
"2f4af31c144bbfab1bbf479866acd820": "\n警告未作为以下任何模块安装 {{LoopBack}} 连接器“{0}”\n\n {1}\n\n要修正请运行\n\n {{npm install {2} --save}}\n",
"6111399276924ffa3bc9a410cdfcb2e5": "无 {{id}} 名称 {0}",
"75518263527c423b8a12f908a2730b0f": "{{className}} 是必需的",
"791ab3031a73ede03f7d6299a85e8289": "在 {0} 毫秒后连接超时",
"7b277018e43d41bc445731092b91547d": "未连接",
"a2487abefef4259c2131d96cdb8543b1": "连接失败:{0}\n将在下一次请求时重试。",
"b15b20280211ad258d92947f05b6e4a5": "连接器尚未进行初始化。",
"e0e9504e137a3c3339144b51ed76fef2": "连接器定义不正确:应创建 dataSource 的“{{connector}}”成员",
"ec42dca074f1818c447f7ad16e2d01af": "连接的连接器未提供 {0}",
"ba0fd8106eb54de4d003a844206431fd": "不推荐使用模型挂钩“{0}”,请改用操作挂钩。{{http://docs.strongloop.com/display/LB/Operation+hooks}}",
"280f4550f90e133118955ec6f6f72830": "已指定鉴别器类型 {0},但是不存在具有此类名称的模型",
"38dbf42c29a4645238cc3d632e88ebc9": "未针对关系 {0} 定义 {{Relation.modelTo}},并且不是 {{polymorphic}}",
"eb56c2b0c30cf006e2df00a549ec9c2c": "未针对 {1} 模型定义关系“{0}”",
"938401ea4ce48159efa9be1d4a5e8bab": "项必须是数组:{0}",
"f41bd91dc0f000a79c0bf842f1b7fdf9": "无法从 JSON 字符串创建列表:{0}",
"514985b2327f061ffb1c932f6b909979": "未定义模型 {0}。",
"cc09e361de7b0f3dc1687515c5713885": "不支持包含点的属性名称。模型:{0},属性:{1}",
"da02dd6c53d4148320eeb31718a7aebe": "属性 {0} 的类型无效",
"ddf0aa14803f1c84f4a97f3803f7471c": "类名是必需的",
"1853f9414bc61ed196952f5c81e41802": "不支持包含点的属性名称。模型:{0},动态属性:{1}",
"bdb11cc1c780c9ccac33c316cfdc9d82": "未针对属性 {0} 定义类型。{1}",
"cd930369e86cdd222f7bd117c6f9fa94": "未知的缺省值提供程序 {0}",
"0c0b867aca0973ba26e887d3337cc4ec": "找不到 {{Polymorphic}} 模型:未设置“{0}”",
"2f062cbecdf24245731bddc77714c814": "无法找到 {1} 的“{0}”关系",
"3cde8cc9bca22c67278b202ab0720106": "对于 {1},找不到具有标识 {0} 的实例",
"6502a117987610380b9068ef98b1b0ee": "在 {0} 中找不到以下项的记录:({1}. {2}{3}.{4}",
"6fcc2ff0db7a4f490f5e0ce9e24691f3": "{{HasOne}} 关系无法创建 {0} 的多个实例",
"7e9530c0399289be0ee601a604be71ff": "{{BelongsTo}} 关系 {0} 为空",
"7faa840eb6ce11250a141deb42a6c489": "未知的关系 {{scope}}{0}",
"89afd3a9249f5a8d3edda07d84ca049d": "找不到 {{Polymorphic}} 模型:“{0}”",
"a004f310d315e592843776fab964eaeb": "{{Polymorphic}} 关系需要直通模型",
"a25e41a39c60c4702e55d0c3936576a1": "键不匹配:{0}.{1}{2}{3}.{4}{5}",
"a327355560d495454fba2c1aad6bdf09": "未知的作用域方法:{0}",
"e08ab0e1ab55f26c357061447b635905": "在 {0} 中找不到以下项的关系:({1}. {2}{3}.{4}",
"e55937649d8d7a11706b8cec22d02eae": "{{HasOne}} 关系 {0} 为空",
"2c4904377a87fdab502118719cc0d266": "不支持 {{Transaction}}",
"ecb7aa804bf54c682999d20d6436104c": "{{transaction}} 不活动:{0}",
"89bf6d92731fe7bd2146ce8d0bec205c": "无效的自变量,必须是字符串、{{regex}} 字面值或 {{RegExp}} 对象",
"8c5ab01638c1ac1d58168c6346a8481a": "无效的 {{regex}} 标志:{0}",
"a6c18a7f4390cd3d59a2a7a047ae2aab": "运行“{{npm install loopback-datasource-juggler}} {0}”命令",
"b138294f132edfe1eb2a8211150c7238": "查询中存在意外的“未定义”",
"8a39126103a157f501affa070367a1b0": "{0} 实例无效。详细信息:{1}。"
}

View File

@ -0,0 +1,74 @@
{
"0bd753a8944ad0af85a939bb25273887": "無法使不明索引鍵 {0} 到期",
"bec226891a505828bfc76c5cfd73b336": "無法取得不明索引鍵 {0} 的 TTL",
"4c78325cedbb826db3a05bf5df0e8546": "更換時必須提供 {{id}}",
"a0cf0e09c26df14283223e84e6a10f00": "無法更新屬性。{{id}} 為 {0} 的 {{Object}} 不存在!",
"e54d944c2a2c85a23caa86027ae307cf": "無法移轉未連接至這個資料來源的模型:{0}",
"e6161ae8459c79d810e2aa9d21282a39": "更新屬性時必須提供 {{id}}",
"fca4d12faff1035d9d0438d73432571b": "{0}.{1} 的項目重複",
"09483e03b91c8bd58732a74b3ef1ec13": "無效日期:{0}",
"0b16d3ffc42f91b4b9a4b3b50c41c838": "順序 {0} 無效",
"0e88a84c6bc2638fbcc5d3ea95181f99": "不明內容:{0}",
"21095484501dbff31af6556fa6039182": "{{offset/skip}} 參數 {0} 無效",
"416dfbb7b823f51c9f3800be81060b41": "對於 {1},找不到 {{id}} 為 {0} 的實例",
"4e31b1edd10dadb724d83387de0b5062": "{{limit}} 參數 {0} 無效",
"5ec8efeb715a2c34b440f2d76e2cf87d": " {0}",
"62a2d80c405b7fec5f547c448ab1b6ff": "{{order}} {0} 的方向無效",
"63f503acabdc4c7588aab741b8466862": "{0} 內容的子句 {1} 無效",
"67c2bf43b5281ab929617423ea8a6f3e": "連接器 {0} 不支援 {{replaceById}} 作業。這不是 LoopBack 的錯誤。請聯絡連接器的作者,建議透過 GitHub 議題。",
"6c3234937d69763fc7f6bcafccc59bbc": "{{Model::deleteById}} 需要 {{id}} 引數",
"6eb6fd4fbd73394000bc25f5776fd20c": "{{Model::exists}} 需要 {{id}} 引數",
"7bbbdece4eea90e42aa5c0bce295e503": "{{Model::findById}} 需要 {{id}} 引數",
"9da58ab4555ac50f4999883de6470d7d": "{0} 運算子的子句 {1} 無效",
"a829dee089c912e68c18920ba015400c": "警告:在 {{'loaded'}} 作業連結鉤中,無法將 model:{2} 的 {{id}} 內容從 {0} 變更為 {1}",
"a984a076c59e451948b2bcf7a393d860": "警告:在 {{'before save'}} 作業連結鉤中,無法將 model:{2} 的 {{id}} 內容從 {0} 變更為 {1}",
"baf2c8b0c5a574b8a894e9b6304fece1": "where 子句 {0} 不是 {{object}}",
"db03083e9a768388fdbee865249ac67a": "忽略 {{updateOrCreate()}} 中的驗證錯誤:",
"e4434de4bb8f5a3cd1d416e4d80d7e0b": "\"{0}\" {{id}} \"{1}\" 不明。",
"e54f118a75e15e132f16b985274eb46d": "查詢過濾器 {0} 不是 {{object}}",
"f6e8c96c93b9c7687d6c172b3695e898": "無法將 {{id}} 內容 ({0}) 從 {1} 更新為 {2}",
"0be2d39d225b1d8b2a0f92ad5c65c9ac": "未指定 {{polymorphic}} {0} 的模型:{1}",
"2f4af31c144bbfab1bbf479866acd820": "\n警告{{LoopBack}} 連接器 \"{0}\" 未安裝為下列任一模組:\n\n {1}\n\n若要修正請執行\n\n {{npm install {2} --save}}\n",
"6111399276924ffa3bc9a410cdfcb2e5": "沒有 {{id}} 名稱 {0}",
"75518263527c423b8a12f908a2730b0f": "需要 {{className}}",
"791ab3031a73ede03f7d6299a85e8289": "連接 {0} 毫秒之後逾時",
"7b277018e43d41bc445731092b91547d": "未連接",
"a2487abefef4259c2131d96cdb8543b1": "連線失敗:{0}\n將在下一次要求時重試。",
"b15b20280211ad258d92947f05b6e4a5": "尚未起始設定連接器。",
"e0e9504e137a3c3339144b51ed76fef2": "未正確定義連接器:應該建立資料來源的 `{{connector}}` 成員",
"ec42dca074f1818c447f7ad16e2d01af": "連接的連接器未提供 {0}",
"ba0fd8106eb54de4d003a844206431fd": "模型連結鉤 \"{0}\" 已淘汰,請改用作業連結鉤。{{http://docs.strongloop.com/display/LB/Operation+hooks}}",
"280f4550f90e133118955ec6f6f72830": "已指定鑑別器類型 {0},但不存在此名稱的模型",
"38dbf42c29a4645238cc3d632e88ebc9": "{{Relation.modelTo}} 未定義給關係 {0},而且不是 {{polymorphic}}",
"eb56c2b0c30cf006e2df00a549ec9c2c": "未定義 {1} 模型的關係 \"{0}\"",
"938401ea4ce48159efa9be1d4a5e8bab": "Items 必須是陣列:{0}",
"f41bd91dc0f000a79c0bf842f1b7fdf9": "無法從 JSON 字串建立 List{0}",
"514985b2327f061ffb1c932f6b909979": "模型 {0} 未定義。",
"cc09e361de7b0f3dc1687515c5713885": "不支援含有點的內容名稱。模型:{0},內容:{1}",
"da02dd6c53d4148320eeb31718a7aebe": "內容 {0} 的類型無效",
"ddf0aa14803f1c84f4a97f3803f7471c": "需要類別名稱",
"1853f9414bc61ed196952f5c81e41802": "不支援含有點的內容名稱。模型:{0},動態內容:{1}",
"bdb11cc1c780c9ccac33c316cfdc9d82": "未定義內容 {0}.{1} 的類型",
"cd930369e86cdd222f7bd117c6f9fa94": "預設值提供者 {0} 不明",
"0c0b867aca0973ba26e887d3337cc4ec": "找不到 {{Polymorphic}} 模型:`{0}` 未設定",
"2f062cbecdf24245731bddc77714c814": "找不到 {1} 的 \"{0}\" 關係",
"3cde8cc9bca22c67278b202ab0720106": "對於 {1},找不到 id 為 {0} 的實例",
"6502a117987610380b9068ef98b1b0ee": "在 {0} 中找不到 ({1}.{2} ,{3}.{4}) 的記錄",
"6fcc2ff0db7a4f490f5e0ce9e24691f3": "{{HasOne}} 關係無法建立多個 {0} 實例",
"7e9530c0399289be0ee601a604be71ff": "{{BelongsTo}} 關係 {0} 是空的",
"7faa840eb6ce11250a141deb42a6c489": "不明關係 {{scope}}{0}",
"89afd3a9249f5a8d3edda07d84ca049d": "找不到 {{Polymorphic}} 模型:`{0}`",
"a004f310d315e592843776fab964eaeb": "{{Polymorphic}} 關係需要有通過模型",
"a25e41a39c60c4702e55d0c3936576a1": "索引鍵不符:{0}.{1}{2}{3}.{4}{5}",
"a327355560d495454fba2c1aad6bdf09": "不明範圍方法:{0}",
"e08ab0e1ab55f26c357061447b635905": "在 {0} 中找不到 ({1}.{2},{3}.{4}) 的關係",
"e55937649d8d7a11706b8cec22d02eae": "{{HasOne}} 關係 {0} 是空的",
"2c4904377a87fdab502118719cc0d266": "不支援 {{Transaction}}",
"ecb7aa804bf54c682999d20d6436104c": "{{transaction}} 為非作用中:{0}",
"89bf6d92731fe7bd2146ce8d0bec205c": "引數無效,必須是字串、{{regex}} 文字或 {{RegExp}} 物件",
"8c5ab01638c1ac1d58168c6346a8481a": "無效 {{regex}} 旗標:{0}",
"a6c18a7f4390cd3d59a2a7a047ae2aab": "執行 \"{{npm install loopback-datasource-juggler}} {0}\" 指令",
"b138294f132edfe1eb2a8211150c7238": "查詢中有非預期的 `undefined`",
"8a39126103a157f501affa070367a1b0": "{0} 實例無效。詳細資料:{1}。"
}

View File

@ -1,3 +1,9 @@
// Copyright IBM Corp. 2015,2016. All Rights Reserved.
// Node module: loopback-datasource-juggler
// This file is licensed under the MIT License.
// License text available at https://opensource.org/licenses/MIT
'use strict';
// A lightweight alternative to "depd" that works in the browser
module.exports = function depd(namespace) {
var warned = {};

223
lib/connectors/kv-memory.js Normal file
View File

@ -0,0 +1,223 @@
'use strict';
var g = require('strong-globalize')();
var assert = require('assert');
var Connector = require('loopback-connector').Connector;
var debug = require('debug')('loopback:connector:kv-memory');
var minimatch = require('minimatch');
var util = require('util');
exports.initialize = function initializeDataSource(dataSource, cb) {
var settings = dataSource.settings;
dataSource.connector = new KeyValueMemoryConnector(settings, dataSource);
if (cb) process.nextTick(cb);
};
function KeyValueMemoryConnector(settings, dataSource) {
Connector.call(this, 'kv-memory', settings);
debug('Connector settings', settings);
this.dataSource = dataSource;
this.DataAccessObject = dataSource.juggler.KeyValueAccessObject;
this._store = Object.create(null);
this._setupRegularCleanup();
};
util.inherits(KeyValueMemoryConnector, Connector);
KeyValueMemoryConnector.prototype._setupRegularCleanup = function() {
// Scan the database for expired keys at a regular interval
// in order to release memory. Note that GET operation checks
// key expiration too, the scheduled cleanup is merely a performance
// optimization.
var self = this;
var timer = this._cleanupTimer = setInterval(
function() {
if (self && self._removeExpiredItems) {
self._removeExpiredItems();
} else {
// The datasource/connector was destroyed - cancel the timer
clearInterval(timer);
}
},
1000);
this._cleanupTimer.unref();
};
KeyValueMemoryConnector._removeExpiredItems = function() {
debug('Running scheduled cleanup of expired items.');
for (var modelName in this._store) {
var modelStore = this._store[modelName];
for (var key in modelStore) {
if (modelStore[key].isExpired()) {
debug('Removing expired key', key);
delete modelStore[key];
}
}
}
};
KeyValueMemoryConnector.prototype._getStoreForModel = function(modelName) {
if (!(modelName in this._store)) {
this._store[modelName] = Object.create(null);
}
return this._store[modelName];
};
KeyValueMemoryConnector.prototype._removeIfExpired = function(modelName, key) {
var store = this._getStoreForModel(modelName);
var item = store[key];
if (item && item.isExpired()) {
debug('Removing expired key', key);
delete store[key];
item = undefined;
return true;
}
return false;
};
KeyValueMemoryConnector.prototype.get =
function(modelName, key, options, callback) {
this._removeIfExpired(modelName, key);
var store = this._getStoreForModel(modelName);
var item = store[key];
var value = item ? item.value : null;
debug('GET %j %j -> %s', modelName, key, value);
if (/^buffer:/.test(value)) {
value = new Buffer(value.slice(7), 'base64');
} else if (/^date:/.test(value)) {
value = new Date(value.slice(5));
} else if (value != null) {
value = JSON.parse(value);
}
process.nextTick(function() {
callback(null, value);
});
};
KeyValueMemoryConnector.prototype.set =
function(modelName, key, value, options, callback) {
var store = this._getStoreForModel(modelName);
var value;
if (Buffer.isBuffer(value)) {
value = 'buffer:' + value.toString('base64');
} else if (value instanceof Date) {
value = 'date:' + value.toISOString();
} else {
value = JSON.stringify(value);
}
debug('SET %j %j %s %j', modelName, key, value, options);
store[key] = new StoreItem(value, options && options.ttl);
process.nextTick(callback);
};
KeyValueMemoryConnector.prototype.expire =
function(modelName, key, ttl, options, callback) {
this._removeIfExpired(modelName, key);
var store = this._getStoreForModel(modelName);
if (!(key in store)) {
return process.nextTick(function() {
var err = new Error(g.f('Cannot expire unknown key %j', key));
err.statusCode = 404;
callback(err);
});
}
debug('EXPIRE %j %j %s', modelName, key, ttl || '(never)');
store[key].setTtl(ttl);
process.nextTick(callback);
};
KeyValueMemoryConnector.prototype.ttl =
function(modelName, key, options, callback) {
this._removeIfExpired(modelName, key);
var store = this._getStoreForModel(modelName);
// key is unknown
if (!(key in store)) {
return process.nextTick(function() {
var err = new Error(g.f('Cannot get TTL for unknown key %j', key));
err.statusCode = 404;
callback(err);
});
}
var ttl = store[key].getTtl();
debug('TTL %j %j -> %s', modelName, key, ttl);
process.nextTick(function() {
callback(null, ttl);
});
};
KeyValueMemoryConnector.prototype.iterateKeys =
function(modelName, filter, options, callback) {
var store = this._getStoreForModel(modelName);
var self = this;
var checkFilter = createMatcher(filter.match);
var keys = Object.keys(store).filter(function(key) {
return !self._removeIfExpired(modelName, key) && checkFilter(key);
});
debug('ITERATE KEYS %j -> %s keys', modelName, keys.length);
var ix = 0;
return {
next: function(cb) {
var value = ix < keys.length ? keys[ix++] : undefined;
setImmediate(function() { cb(null, value); });
},
};
};
function createMatcher(pattern) {
if (!pattern) return function matchAll() { return true; };
return minimatch.filter(pattern, {
nobrace: true,
noglobstar: true,
dot: true,
noext: true,
nocomment: true,
});
}
KeyValueMemoryConnector.prototype.disconnect = function(callback) {
if (this._cleanupTimer)
clearInterval(this._cleanupTimer);
this._cleanupTimer = null;
process.nextTick(callback);
};
function StoreItem(value, ttl) {
this.value = value;
this.setTtl(ttl);
}
StoreItem.prototype.isExpired = function() {
return this.expires && this.expires <= Date.now();
};
StoreItem.prototype.setTtl = function(ttl) {
if (ttl) {
this.expires = Date.now() + ttl;
} else {
this.expires = undefined;
}
};
StoreItem.prototype.getTtl = function() {
return !this.expires ? undefined : this.expires - Date.now();
};

View File

@ -1,3 +1,10 @@
// Copyright IBM Corp. 2013,2016. All Rights Reserved.
// Node module: loopback-datasource-juggler
// This file is licensed under the MIT License.
// License text available at https://opensource.org/licenses/MIT
'use strict';
var g = require('strong-globalize')();
var util = require('util');
var Connector = require('loopback-connector').Connector;
var geo = require('../geo');
@ -44,7 +51,7 @@ Memory.prototype.getTypes = function() {
return ['db', 'nosql', 'memory'];
};
Memory.prototype.connect = function (callback) {
Memory.prototype.connect = function(callback) {
if (this.isTransaction) {
this.onTransactionExec = callback;
} else {
@ -53,17 +60,17 @@ Memory.prototype.connect = function (callback) {
};
function serialize(obj) {
if(obj === null || obj === undefined) {
if (obj === null || obj === undefined) {
return obj;
}
return JSON.stringify(obj);
}
function deserialize(dbObj) {
if(dbObj === null || dbObj === undefined) {
if (dbObj === null || dbObj === undefined) {
return dbObj;
}
if(typeof dbObj === 'string') {
if (typeof dbObj === 'string') {
return JSON.parse(dbObj);
} else {
return dbObj;
@ -76,12 +83,12 @@ Memory.prototype.getCollection = function(model) {
model = modelClass.settings.memory.collection || model;
}
return model;
}
};
Memory.prototype.initCollection = function(model) {
this.collection(model, {});
this.collectionSeq(model, 1);
}
};
Memory.prototype.collection = function(model, val) {
model = this.getCollection(model);
@ -101,14 +108,14 @@ Memory.prototype.loadFromFile = function(callback) {
var localStorage = hasLocalStorage && this.settings.localStorage;
if (self.settings.file) {
fs.readFile(self.settings.file, {encoding: 'utf8', flag: 'r'}, function (err, data) {
fs.readFile(self.settings.file, {encoding: 'utf8', flag: 'r'}, function(err, data) {
if (err && err.code !== 'ENOENT') {
callback && callback(err);
} else {
parseAndLoad(data);
}
});
} else if(localStorage) {
} else if (localStorage) {
var data = window.localStorage.getItem(localStorage);
data = data || '{}';
parseAndLoad(data);
@ -120,14 +127,14 @@ Memory.prototype.loadFromFile = function(callback) {
if (data) {
try {
data = JSON.parse(data.toString());
} catch(e) {
} catch (e) {
return callback(e);
}
self.ids = data.ids || {};
self.cache = data.models || {};
} else {
if(!self.cache) {
if (!self.cache) {
self.ids = {};
self.cache = {};
}
@ -140,22 +147,22 @@ Memory.prototype.loadFromFile = function(callback) {
* Flush the cache into the json file if necessary
* @param {Function} callback
*/
Memory.prototype.saveToFile = function (result, callback) {
Memory.prototype.saveToFile = function(result, callback) {
var self = this;
var file = this.settings.file;
var hasLocalStorage = typeof window !== 'undefined' && window.localStorage;
var localStorage = hasLocalStorage && this.settings.localStorage;
if (file) {
if(!self.writeQueue) {
if (!self.writeQueue) {
// Create a queue for writes
self.writeQueue = async.queue(function (task, cb) {
self.writeQueue = async.queue(function(task, cb) {
// Flush out the models/ids
var data = JSON.stringify({
ids: self.ids,
models: self.cache
models: self.cache,
}, null, ' ');
fs.writeFile(self.settings.file, data, function (err) {
fs.writeFile(self.settings.file, data, function(err) {
cb(err);
task.callback && task.callback(err, task.data);
});
@ -164,20 +171,20 @@ Memory.prototype.saveToFile = function (result, callback) {
// Enqueue the write
self.writeQueue.push({
data: result,
callback: callback
callback: callback,
});
} else if (localStorage) {
// Flush out the models/ids
var data = JSON.stringify({
ids: self.ids,
models: self.cache
models: self.cache,
}, null, ' ');
window.localStorage.setItem(localStorage, data);
process.nextTick(function () {
process.nextTick(function() {
callback && callback(null, result);
});
} else {
process.nextTick(function () {
process.nextTick(function() {
callback && callback(null, result);
});
}
@ -186,10 +193,10 @@ Memory.prototype.saveToFile = function (result, callback) {
Memory.prototype.define = function defineModel(definition) {
this.constructor.super_.prototype.define.apply(this, [].slice.call(arguments));
var m = definition.model.modelName;
if(!this.collection(m)) this.initCollection(m);
if (!this.collection(m)) this.initCollection(m);
};
Memory.prototype.create = function create(model, data, options, callback) {
Memory.prototype._createSync = function(model, data, fn) {
// FIXME: [rfeng] We need to generate unique ids based on the id type
// FIXME: [rfeng] We don't support composite ids yet
var currentId = this.collectionSeq(model);
@ -207,33 +214,102 @@ Memory.prototype.create = function create(model, data, options, callback) {
var idName = this.idName(model);
id = (props[idName] && props[idName].type && props[idName].type(id)) || id;
this.setIdValue(model, data, id);
if(!this.collection(model)) {
if (!this.collection(model)) {
this.collection(model, {});
}
if (this.collection(model)[id]) {
return process.nextTick(function() {
callback(new Error('Duplicate entry for ' + model + '.' + idName));
if (this.collection(model)[id])
return fn(new Error(g.f('Duplicate entry for %s.%s', model, idName)));
this.collection(model)[id] = serialize(data);
fn(null, id);
};
Memory.prototype.create = function create(model, data, options, callback) {
var self = this;
this._createSync(model, data, function(err, id) {
if (err) {
return process.nextTick(function() {
callback(err);
});
};
self.saveToFile(id, callback);
});
};
Memory.prototype.updateOrCreate = function(model, data, options, callback) {
var self = this;
this.exists(model, self.getIdValue(model, data), options, function(err, exists) {
if (exists) {
self.save(model, data, options, function(err, data) {
callback(err, data, {isNewInstance: false});
});
} else {
self.create(model, data, options, function(err, id) {
self.setIdValue(model, data, id);
callback(err, data, {isNewInstance: true});
});
}
});
};
Memory.prototype.patchOrCreateWithWhere =
Memory.prototype.upsertWithWhere = function(model, where, data, options, callback) {
var self = this;
var primaryKey = this.idName(model);
var filter = {where: where};
var nodes = self._findAllSkippingIncludes(model, filter);
if (nodes.length === 0) {
return self._createSync(model, data, function(err, id) {
if (err) return process.nextTick(function() { callback(err); });
self.saveToFile(id, function(err, id) {
self.setIdValue(model, data, id);
callback(err, self.fromDb(model, data), {isNewInstance: true});
});
});
}
if (nodes.length === 1) {
var primaryKeyValue = nodes[0][primaryKey];
self.updateAttributes(model, primaryKeyValue, data, options, function(err, data) {
callback(err, data, {isNewInstance: false});
});
} else {
process.nextTick(function() {
var error = new Error('There are multiple instances found.' +
'Upsert Operation will not be performed!');
error.statusCode = 400;
callback(error);
});
}
};
Memory.prototype.findOrCreate = function(model, filter, data, callback) {
var self = this;
var nodes = self._findAllSkippingIncludes(model, filter);
var found = nodes[0];
if (!found) {
// Calling _createSync to update the collection in a sync way and to guarantee to create it in the same turn of even loop
return self._createSync(model, data, function(err, id) {
if (err) return callback(err);
self.saveToFile(id, function(err, id) {
self.setIdValue(model, data, id);
callback(err, data, true);
});
});
}
this.collection(model)[id] = serialize(data);
this.saveToFile(id, callback);
};
if (!filter || !filter.include) {
return process.nextTick(function() {
callback(null, found, false);
});
}
Memory.prototype.updateOrCreate = function (model, data, options, callback) {
var self = this;
this.exists(model, self.getIdValue(model, data), options, function (err, exists) {
if (exists) {
self.save(model, data, options, function(err, data) {
callback(err, data, { isNewInstance: false });
});
} else {
self.create(model, data, options, function (err, id) {
self.setIdValue(model, data, id);
callback(err, data, { isNewInstance: true });
});
}
self._models[model].model.include(nodes[0], filter.include, {}, function(err, nodes) {
process.nextTick(function() {
if (err) return callback(err);
callback(null, nodes[0], false);
});
});
};
@ -248,18 +324,18 @@ Memory.prototype.save = function save(model, data, options, callback) {
}
this.collection(model)[id] = serialize(data);
this.saveToFile(data, function(err) {
callback(err, self.fromDb(model, data), { isNewInstance: !modelData });
callback(err, self.fromDb(model, data), {isNewInstance: !modelData});
});
};
Memory.prototype.exists = function exists(model, id, options, callback) {
process.nextTick(function () {
process.nextTick(function() {
callback(null, this.collection(model) && this.collection(model).hasOwnProperty(id));
}.bind(this));
};
Memory.prototype.find = function find(model, id, options, callback) {
process.nextTick(function () {
process.nextTick(function() {
callback(null, id in this.collection(model) && this.fromDb(model, this.collection(model)[id]));
}.bind(this));
};
@ -267,36 +343,72 @@ Memory.prototype.find = function find(model, id, options, callback) {
Memory.prototype.destroy = function destroy(model, id, options, callback) {
var exists = this.collection(model)[id];
delete this.collection(model)[id];
this.saveToFile({ count: exists ? 1 : 0 }, callback);
this.saveToFile({count: exists ? 1 : 0}, callback);
};
Memory.prototype.fromDb = function (model, data) {
Memory.prototype.fromDb = function(model, data) {
if (!data) return null;
data = deserialize(data);
var props = this._models[model].properties;
for (var key in data) {
var val = data[key];
if (val === undefined || val === null) {
continue;
try {
for (var key in data) {
data[key] = this._castPropertyValue(key, data[key], props);
}
if (props[key]) {
switch (props[key].type.name) {
case 'Date':
val = new Date(val.toString().replace(/GMT.*$/, 'GMT'));
break;
case 'Boolean':
val = Boolean(val);
break;
case 'Number':
val = Number(val);
break;
}
}
data[key] = val;
} catch (err) {
// Modify error message and re-throw
err.message = g.f('Unable to convert to instance of "%s": %s', model,
err);
throw err;
}
return data;
};
Memory.prototype._castPropertyValue = function(prop, val, props) {
var self = this;
if (val === undefined || val === null || !props[prop]) {
return val;
}
if (Array.isArray(val)) {
return val.map(function(val) {
return self._castPropertyValue(prop, val, props);
});
}
var isArray = Array.isArray(props[prop].type);
var propType = isArray ? props[prop].type[0] : props[prop].type;
if (!propType || !propType.name) {
if (isArray)
throw new Error(g.f(
'Property definition "%s" did not specify any sub-types!', prop));
else
throw new Error(g.f(
'Property definition "%s" was null or undefined!', prop
));
}
switch (propType.name) {
case 'Date':
val = new Date(val.toString().replace(/GMT.*$/, 'GMT'));
break;
case 'Boolean':
val = Boolean(val);
break;
case 'Number':
val = Number(val);
break;
case 'ModelConstructor':
for (var subProp in val) {
if (propType.definition && propType.definition.properties)
val[subProp] = this._castPropertyValue(subProp, val[subProp],
propType.definition.properties);
}
break;
}
return val;
};
function getValue(obj, path) {
if (obj == null) {
return undefined;
@ -312,9 +424,8 @@ function getValue(obj, path) {
return val;
}
Memory.prototype.all = function all(model, filter, options, callback) {
var self = this;
var nodes = Object.keys(this.collection(model)).map(function (key) {
Memory.prototype._findAllSkippingIncludes = function(model, filter) {
var nodes = Object.keys(this.collection(model)).map(function(key) {
return this.fromDb(model, this.collection(model)[key]);
}.bind(this));
@ -328,17 +439,17 @@ Memory.prototype.all = function all(model, filter, options, callback) {
// do we need some sorting?
if (filter.order) {
var orders = filter.order;
if (typeof filter.order === "string") {
if (typeof filter.order === 'string') {
orders = [filter.order];
}
orders.forEach(function (key, i) {
orders.forEach(function(key, i) {
var reverse = 1;
var m = key.match(/\s+(A|DE)SC$/i);
if (m) {
key = key.replace(/\s+(A|DE)SC/i, '');
if (m[1].toLowerCase() === 'de') reverse = -1;
}
orders[i] = {"key": key, "reverse": reverse};
orders[i] = {'key': key, 'reverse': reverse};
});
nodes = nodes.sort(sorting.bind(orders));
}
@ -364,14 +475,7 @@ Memory.prototype.all = function all(model, filter, options, callback) {
var limit = filter.limit || nodes.length;
nodes = nodes.slice(skip, skip + limit);
}
process.nextTick(function () {
if (filter && filter.include) {
self._models[model].model.include(nodes, filter.include, options, callback);
} else {
callback(null, nodes);
}
});
return nodes;
function sorting(a, b) {
var undefinedA, undefinedB;
@ -393,22 +497,35 @@ Memory.prototype.all = function all(model, filter, options, callback) {
}
};
Memory.prototype.all = function all(model, filter, options, callback) {
var self = this;
var nodes = self._findAllSkippingIncludes(model, filter);
process.nextTick(function() {
if (filter && filter.include) {
self._models[model].model.include(nodes, filter.include, options, callback);
} else {
callback(null, nodes);
}
});
};
function applyFilter(filter) {
var where = filter.where;
if (typeof where === 'function') {
return where;
}
var keys = Object.keys(where);
return function (obj) {
return function(obj) {
return keys.every(function(key) {
if(key === 'and' || key === 'or') {
if(Array.isArray(where[key])) {
if(key === 'and') {
if (key === 'and' || key === 'or') {
if (Array.isArray(where[key])) {
if (key === 'and') {
return where[key].every(function(cond) {
return applyFilter({where: cond})(obj);
});
}
if(key === 'or') {
if (key === 'or') {
return where[key].some(function(cond) {
return applyFilter({where: cond})(obj);
});
@ -426,14 +543,13 @@ function applyFilter(filter) {
if (matcher.neq !== undefined && value.length <= 0) {
return true;
}
return value.some(function (v, i) {
return value.some(function(v, i) {
var filter = {where: {}};
filter.where[i] = matcher;
return applyFilter(filter)(value);
});
}
if (test(where[key], value)) {
return true;
}
@ -442,16 +558,20 @@ function applyFilter(filter) {
// then, we attempt to emulate mongo db matching. Helps for embedded relations
var dotIndex = key.indexOf('.');
var subValue = obj[key.substring(0, dotIndex)];
if (dotIndex !== -1 && Array.isArray(subValue)) {
if (dotIndex !== -1) {
var subFilter = {where: {}};
var subKey = key.substring(dotIndex+1);
var subKey = key.substring(dotIndex + 1);
subFilter.where[subKey] = where[key];
return subValue.some(applyFilter(subFilter));
if (Array.isArray(subValue)) {
return subValue.some(applyFilter(subFilter));
} else if (typeof subValue === 'object' && subValue !== null) {
return applyFilter(subFilter)(subValue);
}
}
return false;
});
}
};
function toRegExp(pattern) {
if (pattern instanceof RegExp) {
@ -460,7 +580,7 @@ function applyFilter(filter) {
var regex = '';
// Escaping user input to be treated as a literal string within a regular expression
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions#Writing_a_Regular_Expression_Pattern
pattern = pattern.replace(/([.*+?^=!:${}()|\[\]\/\\])/g, "\\$1");
pattern = pattern.replace(/([.*+?^=!:${}()|\[\]\/\\])/g, '\\$1');
for (var i = 0, n = pattern.length; i < n; i++) {
var char = pattern.charAt(i);
if (char === '\\') {
@ -477,8 +597,7 @@ function applyFilter(filter) {
regex += '\\.';
} else if (char === '*') {
regex += '\\*';
}
else {
} else {
regex += char;
}
}
@ -518,14 +637,13 @@ function applyFilter(filter) {
return compare(example.neq, value) !== 0;
}
if ('between' in example ) {
return ( testInEquality({gte:example.between[0]}, value) &&
testInEquality({lte:example.between[1]}, value) );
if ('between' in example) {
return (testInEquality({gte: example.between[0]}, value) &&
testInEquality({lte: example.between[1]}, value));
}
if (example.like || example.nlike) {
var like = example.like || example.nlike;
if (example.like || example.nlike || example.ilike || example.nilike) {
var like = example.like || example.nlike || example.ilike || example.nilike;
if (typeof like === 'string') {
like = toRegExp(like);
}
@ -536,6 +654,14 @@ function applyFilter(filter) {
if (example.nlike) {
return !new RegExp(like).test(value);
}
if (example.ilike) {
return !!new RegExp(like, 'i').test(value);
}
if (example.nilike) {
return !new RegExp(like, 'i').test(value);
}
}
if (testInEquality(example, value)) {
@ -543,8 +669,8 @@ function applyFilter(filter) {
}
}
// not strict equality
return (example !== null ? example.toString() : example)
== (value != null ? value.toString() : value);
return (example !== null ? example.toString() : example) ==
(value != null ? value.toString() : value);
}
/**
@ -555,7 +681,7 @@ function applyFilter(filter) {
* @private
*/
function compare(val1, val2) {
if(val1 == null || val2 == null) {
if (val1 == null || val2 == null) {
// Either val1 or val2 is null or undefined
return val1 == val2 ? 0 : NaN;
}
@ -599,7 +725,7 @@ Memory.prototype.destroyAll = function destroyAll(model, where, options, callbac
var count = 0;
if (where) {
filter = applyFilter({where: where});
Object.keys(cache).forEach(function (id) {
Object.keys(cache).forEach(function(id) {
if (!filter || filter(this.fromDb(model, cache[id]))) {
count++;
delete cache[id];
@ -609,7 +735,7 @@ Memory.prototype.destroyAll = function destroyAll(model, where, options, callbac
count = Object.keys(cache).length;
this.collection(model, {});
}
this.saveToFile({ count: count }, callback);
this.saveToFile({count: count}, callback);
};
Memory.prototype.count = function count(model, where, options, callback) {
@ -617,12 +743,12 @@ Memory.prototype.count = function count(model, where, options, callback) {
var data = Object.keys(cache);
if (where) {
var filter = {where: where};
data = data.map(function (id) {
data = data.map(function(id) {
return this.fromDb(model, cache[id]);
}.bind(this));
data = data.filter(applyFilter(filter));
}
process.nextTick(function () {
process.nextTick(function() {
callback(null, data.length);
});
};
@ -637,7 +763,7 @@ Memory.prototype.update =
var ids = Object.keys(cache);
var count = 0;
async.each(ids, function (id, done) {
async.each(ids, function(id, done) {
var inst = self.fromDb(model, cache[id]);
if (!filter || filter(inst)) {
count++;
@ -648,7 +774,7 @@ Memory.prototype.update =
} else {
process.nextTick(done);
}
}, function (err) {
}, function(err) {
if (err) return cb(err);
self.saveToFile({count: count}, cb);
});
@ -656,7 +782,7 @@ Memory.prototype.update =
Memory.prototype.updateAttributes = function updateAttributes(model, id, data, options, cb) {
if (!id) {
var err = new Error('You must provide an id when updating attributes!');
var err = new Error(g.f('You must provide an {{id}} when updating attributes!'));
if (cb) {
return cb(err);
} else {
@ -675,24 +801,82 @@ Memory.prototype.updateAttributes = function updateAttributes(model, id, data, o
if (modelData) {
this.save(model, data, options, cb);
} else {
cb(new Error('Could not update attributes. Object with id ' + id + ' does not exist!'));
cb(new Error(g.f('Could not update attributes. {{Object}} with {{id}} %s does not exist!', id)));
}
};
Memory.prototype.transaction = function () {
Memory.prototype.replaceById = function(model, id, data, options, cb) {
var self = this;
if (!id) {
var err = new Error(g.f('You must provide an {{id}} when replacing!'));
return process.nextTick(function() { cb(err); });
}
// Do not modify the data object passed in arguments
data = Object.create(data);
this.setIdValue(model, data, id);
var cachedModels = this.collection(model);
var modelData = cachedModels && this.collection(model)[id];
if (!modelData) {
var msg = 'Could not replace. Object with id ' + id + ' does not exist!';
return process.nextTick(function() { cb(new Error(msg)); });
}
var newModelData = {};
for (var key in data) {
var val = data[key];
if (typeof val === 'function') {
continue; // Skip methods
}
newModelData[key] = val;
}
this.collection(model)[id] = serialize(newModelData);
this.saveToFile(newModelData, function(err) {
cb(err, self.fromDb(model, newModelData));
});
};
Memory.prototype.replaceOrCreate = function(model, data, options, callback) {
var self = this;
var idName = self.idNames(model)[0];
var idValue = self.getIdValue(model, data);
var filter = {where: {}};
filter.where[idName] = idValue;
var nodes = self._findAllSkippingIncludes(model, filter);
var found = nodes[0];
if (!found) {
// Calling _createSync to update the collection in a sync way and
// to guarantee to create it in the same turn of even loop
return self._createSync(model, data, function(err, id) {
if (err) return process.nextTick(function() { callback(err); });
self.saveToFile(id, function(err, id) {
self.setIdValue(model, data, id);
callback(err, self.fromDb(model, data), {isNewInstance: true});
});
});
}
var id = self.getIdValue(model, data);
self.collection(model)[id] = serialize(data);
self.saveToFile(data, function(err) {
callback(err, self.fromDb(model, data), {isNewInstance: false});
});
};
Memory.prototype.transaction = function() {
return new Memory(this);
};
Memory.prototype.exec = function (callback) {
Memory.prototype.exec = function(callback) {
this.onTransactionExec();
setTimeout(callback, 50);
};
Memory.prototype.buildNearFilter = function (filter) {
Memory.prototype.buildNearFilter = function(filter) {
// noop
}
};
Memory.prototype.automigrate = function (models, cb) {
Memory.prototype.automigrate = function(models, cb) {
var self = this;
if ((!cb) && ('function' === typeof models)) {
@ -715,8 +899,8 @@ Memory.prototype.automigrate = function (models, cb) {
if (invalidModels.length) {
return process.nextTick(function() {
cb(new Error('Cannot migrate models not attached to this datasource: ' +
invalidModels.join(' ')));
cb(new Error(g.f('Cannot migrate models not attached to this datasource: %s',
invalidModels.join(' '))));
});
}
@ -724,7 +908,7 @@ Memory.prototype.automigrate = function (models, cb) {
self.initCollection(m);
});
if (cb) process.nextTick(cb);
}
};
function merge(base, update) {
if (!base) {
@ -732,9 +916,9 @@ function merge(base, update) {
}
// We cannot use Object.keys(update) if the update is an instance of the model
// class as the properties are defined at the ModelClass.prototype level
for(var key in update) {
for (var key in update) {
var val = update[key];
if(typeof val === 'function') {
if (typeof val === 'function') {
continue; // Skip methods
}
base[key] = val;

View File

@ -1,3 +1,10 @@
// Copyright IBM Corp. 2014,2016. All Rights Reserved.
// Node module: loopback-datasource-juggler
// This file is licensed under the MIT License.
// License text available at https://opensource.org/licenses/MIT
'use strict';
var g = require('strong-globalize')();
var util = require('util');
var Connector = require('loopback-connector').Connector;
var utils = require('../utils');
@ -42,7 +49,7 @@ Transient.prototype.getTypes = function() {
return ['db', 'nosql', 'transient'];
};
Transient.prototype.connect = function (callback) {
Transient.prototype.connect = function(callback) {
if (this.isTransaction) {
this.onTransactionExec = callback;
} else {
@ -58,26 +65,26 @@ Transient.prototype.generateId = function(model, data, idName) {
if (idType === Number) {
return Math.floor(Math.random() * 10000); // max. 4 digits
} else {
return crypto.randomBytes(Math.ceil(24/2))
return crypto.randomBytes(Math.ceil(24 / 2))
.toString('hex') // convert to hexadecimal format
.slice(0, 24); // return required number of characters
}
};
Transient.prototype.exists = function exists(model, id, callback) {
process.nextTick(function () { callback(null, false); }.bind(this));
process.nextTick(function() { callback(null, false); }.bind(this));
};
Transient.prototype.find = function find(model, id, callback) {
process.nextTick(function () { callback(null, null); }.bind(this));
process.nextTick(function() { callback(null, null); }.bind(this));
};
Transient.prototype.all = function all(model, filter, callback) {
process.nextTick(function () { callback(null, []); });
process.nextTick(function() { callback(null, []); });
};
Transient.prototype.count = function count(model, callback, where) {
process.nextTick(function () { callback(null, 0); });
process.nextTick(function() { callback(null, 0); });
};
Transient.prototype.create = function create(model, data, callback) {
@ -99,11 +106,11 @@ Transient.prototype.update =
Transient.prototype.updateAll = function updateAll(model, where, data, cb) {
var count = 0;
this.flush('update', {count: count}, cb);
};
};
Transient.prototype.updateAttributes = function updateAttributes(model, id, data, cb) {
if (!id) {
var err = new Error('You must provide an id when updating attributes!');
var err = new Error(g.f('You must provide an {{id}} when updating attributes!'));
if (cb) {
return cb(err);
} else {
@ -131,15 +138,15 @@ Transient.prototype.destroyAll = function destroyAll(model, where, callback) {
* Flush the cache - noop.
* @param {Function} callback
*/
Transient.prototype.flush = function (action, result, callback) {
process.nextTick(function () { callback && callback(null, result); });
Transient.prototype.flush = function(action, result, callback) {
process.nextTick(function() { callback && callback(null, result); });
};
Transient.prototype.transaction = function () {
Transient.prototype.transaction = function() {
return new Transient(this);
};
Transient.prototype.exec = function (callback) {
Transient.prototype.exec = function(callback) {
this.onTransactionExec();
setTimeout(callback, 50);
};

1496
lib/dao.js

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -1,3 +1,9 @@
// Copyright IBM Corp. 2013,2016. All Rights Reserved.
// Node module: loopback-datasource-juggler
// This file is licensed under the MIT License.
// License text available at https://opensource.org/licenses/MIT
'use strict';
var assert = require('assert');
/*!
@ -8,7 +14,7 @@ exports.nearFilter = function nearFilter(where) {
var result = false;
if (where && typeof where === 'object') {
Object.keys(where).forEach(function (key) {
Object.keys(where).forEach(function(key) {
var ex = where[key];
if (ex && ex.near) {
@ -16,20 +22,20 @@ exports.nearFilter = function nearFilter(where) {
near: ex.near,
maxDistance: ex.maxDistance,
unit: ex.unit,
key: key
key: key,
};
}
});
}
return result;
}
};
/*!
* Filter a set of objects using the given `nearFilter`.
*/
exports.filter = function (arr, filter) {
exports.filter = function(arr, filter) {
var origin = filter.near;
var max = filter.maxDistance > 0 ? filter.maxDistance : false;
var unit = filter.unit;
@ -39,7 +45,7 @@ exports.filter = function (arr, filter) {
var distances = {};
var result = [];
arr.forEach(function (obj) {
arr.forEach(function(obj) {
var loc = obj[key];
// filter out objects without locations
@ -62,7 +68,7 @@ exports.filter = function (arr, filter) {
}
});
return result.sort(function (objA, objB) {
return result.sort(function(objA, objB) {
var a = objA[key];
var b = objB[key];
@ -76,31 +82,31 @@ exports.filter = function (arr, filter) {
return 0;
}
});
}
};
exports.GeoPoint = GeoPoint;
/**
/**
* The GeoPoint object represents a physical location.
*
*
* For example:
*
*
* ```js
* var loopback = require(loopback);
* var here = new loopback.GeoPoint({lat: 10.32424, lng: 5.84978});
* ```
*
*
* Embed a latitude / longitude point in a model.
*
*
* ```js
* var CoffeeShop = loopback.createModel('coffee-shop', {
* location: 'GeoPoint'
* });
* ```
*
*
* You can query LoopBack models with a GeoPoint property and an attached data source using geo-spatial filters and
* sorting. For example, the following code finds the three nearest coffee shops.
*
*
* ```js
* CoffeeShop.attachTo(oracle);
* var here = new GeoPoint({lat: 10.32424, lng: 5.84978});
@ -109,9 +115,9 @@ exports.GeoPoint = GeoPoint;
* });
* ```
* @class GeoPoint
* @property {Number} lat The latitude in degrees.
* @property {Number} lng The longitude in degrees.
*
* @property {Number} lat The latitude in degrees.
* @property {Number} lng The longitude in degrees.
*
* @options {Object} Options Object with two Number properties: lat and long.
* @property {Number} lat The latitude point in degrees. Range: -90 to 90.
* @property {Number} lng The longitude point in degrees. Range: -180 to 180.
@ -126,23 +132,29 @@ function GeoPoint(data) {
return new GeoPoint(data);
}
if(arguments.length === 2) {
if (arguments.length === 2) {
data = {
lat: arguments[0],
lng: arguments[1]
lng: arguments[1],
};
}
assert(Array.isArray(data) || typeof data === 'object' || typeof data === 'string', 'must provide valid geo-coordinates array [lat, lng] or object or a "lat, lng" string');
assert(
Array.isArray(data) ||
typeof data === 'object' ||
typeof data === 'string',
'must provide valid geo-coordinates array [lat, lng] or object or a ' +
'"lat, lng" string');
if (typeof data === 'string') {
data = data.split(/,\s*/);
assert(data.length === 2, 'must provide a string "lat,lng" creating a GeoPoint with a string');
assert(data.length === 2, 'must provide a string "lat,lng" creating a ' +
'GeoPoint with a string');
}
if (Array.isArray(data)) {
data = {
lat: Number(data[0]),
lng: Number(data[1])
lng: Number(data[1]),
};
} else {
data.lng = Number(data.lng);
@ -157,18 +169,18 @@ function GeoPoint(data) {
assert(data.lat <= 90, 'lat must be <= 90');
assert(data.lat >= -90, 'lat must be >= -90');
this.lat = data.lat;
this.lat = data.lat;
this.lng = data.lng;
}
/**
* Determine the spherical distance between two GeoPoints.
*
*
* @param {GeoPoint} pointA Point A
* @param {GeoPoint} pointB Point B
* @options {Object} options Options object with one key, 'type'. See below.
* @property {String} type Unit of measurement, one of:
*
*
* - `miles` (default)
* - `radians`
* - `kilometers`
@ -200,16 +212,16 @@ GeoPoint.distanceBetween = function distanceBetween(a, b, options) {
* Example:
* ```js
* var loopback = require(loopback);
*
*
* var here = new loopback.GeoPoint({lat: 10, lng: 10});
* var there = new loopback.GeoPoint({lat: 5, lng: 5});
*
*
* loopback.GeoPoint.distanceBetween(here, there, {type: 'miles'}) // 438
* ```
* @param {Object} point GeoPoint object to which to measure distance.
* @options {Object} options Options object with one key, 'type'. See below.
* @property {String} type Unit of measurement, one of:
*
*
* - `miles` (default)
* - `radians`
* - `kilometers`
@ -219,7 +231,7 @@ GeoPoint.distanceBetween = function distanceBetween(a, b, options) {
* - `degrees`
*/
GeoPoint.prototype.distanceTo = function (point, options) {
GeoPoint.prototype.distanceTo = function(point, options) {
return GeoPoint.distanceBetween(this, point, options);
};
@ -227,7 +239,7 @@ GeoPoint.prototype.distanceTo = function (point, options) {
* Simple serialization.
*/
GeoPoint.prototype.toString = function () {
GeoPoint.prototype.toString = function() {
return this.lat + ',' + this.lng;
};
@ -250,11 +262,10 @@ var EARTH_RADIUS = {
miles: 3958.75,
feet: 20902200,
radians: 1,
degrees: RAD2DEG
degrees: RAD2DEG,
};
function geoDistance(x1, y1, x2, y2, options) {
var type = (options && options.type) || 'miles';
// Convert to radians
@ -263,7 +274,7 @@ function geoDistance(x1, y1, x2, y2, options) {
x2 = x2 * DEG2RAD;
y2 = y2 * DEG2RAD;
// use the haversine formula to calculate distance for any 2 points on a sphere.
// use the haversine formula to calculate distance for any 2 points on a sphere.
// ref http://en.wikipedia.org/wiki/Haversine_formula
var haversine = function(a) {
return Math.pow(Math.sin(a / 2.0), 2);

View File

@ -1,4 +1,11 @@
// Copyright IBM Corp. 2013,2016. All Rights Reserved.
// Node module: loopback-datasource-juggler
// This file is licensed under the MIT License.
// License text available at https://opensource.org/licenses/MIT
'use strict';
var deprecated = require('depd')('loopback-datasource-juggler');
var g = require('strong-globalize')();
/*!
* Module exports
@ -31,10 +38,10 @@ Hookable.afterDestroy = null;
// TODO: Evaluate https://github.com/bnoguchi/hooks-js/
Hookable.prototype.trigger = function trigger(actionName, work, data, callback) {
var capitalizedName = capitalize(actionName);
var beforeHook = this.constructor["before" + capitalizedName]
|| this.constructor["pre" + capitalizedName];
var afterHook = this.constructor["after" + capitalizedName]
|| this.constructor["post" + capitalizedName];
var beforeHook = this.constructor['before' + capitalizedName] ||
this.constructor['pre' + capitalizedName];
var afterHook = this.constructor['after' + capitalizedName] ||
this.constructor['post' + capitalizedName];
if (actionName === 'validate') {
beforeHook = beforeHook || this.constructor.beforeValidation;
afterHook = afterHook || this.constructor.afterValidation;
@ -52,7 +59,7 @@ Hookable.prototype.trigger = function trigger(actionName, work, data, callback)
if (work) {
if (beforeHook) {
// before hook should be called on instance with two parameters: next and data
beforeHook.call(inst, function () {
beforeHook.call(inst, function() {
// Check arguments to next(err, result)
if (arguments.length) {
return callback && callback.apply(null, arguments);
@ -89,7 +96,7 @@ function deprecateHook(ctor, prefixes, capitalizedName) {
var hookName = candidateNames.filter(function(hook) { return !!ctor[hook]; })[0];
if (!hookName) return; // just to be sure, this should never happen
if (ctor.modelName) hookName = ctor.modelName + '.' + hookName;
deprecated('Model hook "' + hookName + '" is deprecated, ' +
deprecated(g.f('Model hook "%s" is deprecated, ' +
'use Operation hooks instead. ' +
'http://docs.strongloop.com/display/LB/Operation+hooks');
'{{http://docs.strongloop.com/display/LB/Operation+hooks}}', hookName));
}

View File

@ -1,10 +1,18 @@
// Copyright IBM Corp. 2013,2015. All Rights Reserved.
// Node module: loopback-datasource-juggler
// This file is licensed under the MIT License.
// License text available at https://opensource.org/licenses/MIT
'use strict';
var async = require('async');
var g = require('strong-globalize')();
var utils = require('./utils');
var List = require('./list');
var includeUtils = require('./include_utils');
var isPlainObject = utils.isPlainObject;
var defineCachedRelations = utils.defineCachedRelations;
var uniq = utils.uniq;
var idName = utils.idName;
/*!
* Normalize the include to be an array
@ -60,15 +68,6 @@ IncludeScope.prototype.include = function() {
return this._include;
};
/**
* Find the idKey of a Model.
* @param {ModelConstructor} m - Model Constructor
* @returns {String}
*/
function idName(m) {
return m.definition.idName() || 'id';
}
/*!
* Look up a model by name from the list of given models
* @param {Object} models Models keyed by name
@ -95,11 +94,11 @@ function lookupModel(models, modelName) {
function execTasksWithInterLeave(tasks, callback) {
//let's give others some time to process.
//Context Switch BEFORE Heavy Computation
process.nextTick(function () {
process.nextTick(function() {
//Heavy Computation
async.parallel(tasks, function (err, info) {
async.parallel(tasks, function(err, info) {
//Context Switch AFTER Heavy Computation
process.nextTick(function () {
process.nextTick(function() {
callback(err, info);
});
});
@ -151,7 +150,7 @@ Inclusion.normalizeInclude = normalizeInclude;
* @param {Function} cb Callback called when relations are loaded
*
*/
Inclusion.include = function (objects, include, options, cb) {
Inclusion.include = function(objects, include, options, cb) {
if (typeof options === 'function' && cb === undefined) {
cb = options;
options = {};
@ -193,16 +192,14 @@ Inclusion.include = function (objects, include, options, cb) {
subInclude = null;
}
}
}
else {
} else {
relationName = include;
subInclude = null;
}
var relation = relations[relationName];
if (!relation) {
cb(new Error('Relation "' + relationName + '" is not defined for '
+ self.modelName + ' model'));
cb(new Error(g.f('Relation "%s" is not defined for %s model', relationName, self.modelName)));
return;
}
var polymorphic = relation.polymorphic;
@ -213,8 +210,8 @@ Inclusion.include = function (objects, include, options, cb) {
//}
if (!relation.modelTo) {
if (!relation.polymorphic) {
cb(new Error('Relation.modelTo is not defined for relation' +
relationName + ' and is no polymorphic'));
cb(new Error(g.f('{{Relation.modelTo}} is not defined for relation %s and is no {{polymorphic}}',
relationName)));
return;
}
}
@ -231,7 +228,7 @@ Inclusion.include = function (objects, include, options, cb) {
if (filter.fields && Array.isArray(subInclude) &&
relation.modelTo.relations) {
includeScope.fields = [];
subInclude.forEach(function (name) {
subInclude.forEach(function(name) {
var rel = relation.modelTo.relations[name];
if (rel && rel.type === 'belongsTo') {
includeScope.fields.push(rel.keyFrom);
@ -246,8 +243,7 @@ Inclusion.include = function (objects, include, options, cb) {
var fields = filter.fields;
if (Array.isArray(fields) && fields.indexOf(relation.keyTo) === -1) {
fields.push(relation.keyTo);
}
else if (isPlainObject(fields) && !fields[relation.keyTo]) {
} else if (isPlainObject(fields) && !fields[relation.keyTo]) {
fields[relation.keyTo] = true;
}
@ -271,13 +267,12 @@ Inclusion.include = function (objects, include, options, cb) {
}
//This handles exactly hasMany. Fast and straightforward. Without parallel, each and other boilerplate.
if(relation.type === 'hasMany' && relation.multiple && !subInclude){
if (relation.type === 'hasMany' && relation.multiple && !subInclude) {
return includeHasManySimple(cb);
}
//assuming all other relations with multiple=true as hasMany
return includeHasMany(cb);
}
else {
} else {
if (polymorphic) {
if (relation.type === 'hasOne') {
return includePolymorphicHasOne(cb);
@ -316,16 +311,20 @@ Inclusion.include = function (objects, include, options, cb) {
//default filters are not applicable on through model. should be applied
//on modelTo later in 2nd DB call.
var throughFilter = {
where: {}
where: {},
};
throughFilter.where[relation.keyTo] = {
inq: uniq(sourceIds)
inq: uniq(sourceIds),
};
if (polymorphic) {
var throughModel = polymorphic.invert ?
relation.modelTo :
relation.modelFrom;
//handle polymorphic hasMany (reverse) in which case we need to filter
//by discriminator to filter other types
throughFilter.where[polymorphic.discriminator] =
relation.modelFrom.definition.name;
throughModel.definition.name;
}
/**
* 1st DB Call of 2 step process. Get through model objects first
@ -362,14 +361,13 @@ Inclusion.include = function (objects, include, options, cb) {
//Polymorphic relation does not have idKey of modelTo. Find it manually
var modelToIdName = idName(relation.modelTo);
filter.where[modelToIdName] = {
inq: uniq(targetIds)
inq: uniq(targetIds),
};
//make sure that the modelToIdName is included if fields are specified
if (Array.isArray(fields) && fields.indexOf(modelToIdName) === -1) {
fields.push(modelToIdName);
}
else if (isPlainObject(fields) && !fields[modelToIdName]) {
} else if (isPlainObject(fields) && !fields[modelToIdName]) {
fields[modelToIdName] = true;
}
@ -396,7 +394,7 @@ Inclusion.include = function (objects, include, options, cb) {
function linkManyToMany(target, next) {
var targetId = target[modelToIdName];
var objList = targetObjsMap[targetId.toString()];
async.each(objList, function (obj, next) {
async.each(objList, function(obj, next) {
if (!obj) return next();
obj.__cachedRelations[relationName].push(target);
processTargetObj(obj, next);
@ -445,7 +443,7 @@ Inclusion.include = function (objects, include, options, cb) {
obj.__cachedRelations[relationName] = [];
}
filter.where[relation.keyTo] = {
inq: uniq(allTargetIds)
inq: uniq(allTargetIds),
};
relation.applyScope(null, filter);
/**
@ -476,12 +474,11 @@ Inclusion.include = function (objects, include, options, cb) {
async.each(targets, linkManyToMany, next);
function linkManyToMany(target, next) {
var objList = targetObjsMap[target[relation.keyTo].toString()];
async.each(objList, function (obj, next) {
async.each(objList, function(obj, next) {
if (!obj) return next();
obj.__cachedRelations[relationName].push(target);
processTargetObj(obj, next);
}, next);
}
}
@ -498,21 +495,21 @@ Inclusion.include = function (objects, include, options, cb) {
var objIdMap2 = includeUtils.buildOneToOneIdentityMapWithOrigKeys(objs, relation.keyFrom);
filter.where[relation.keyTo] = {
inq: uniq(objIdMap2.getKeys())
inq: uniq(objIdMap2.getKeys()),
};
relation.applyScope(null, filter);
relation.modelTo.find(filter, options, targetFetchHandler);
function targetFetchHandler(err, targets) {
if(err) {
if (err) {
return callback(err);
}
var targetsIdMap = includeUtils.buildOneToManyIdentityMapWithOrigKeys(targets, relation.keyTo);
includeUtils.join(objIdMap2, targetsIdMap, function(obj1, valueToMergeIn){
includeUtils.join(objIdMap2, targetsIdMap, function(obj1, valueToMergeIn) {
defineCachedRelations(obj1);
obj1.__cachedRelations[relationName] = valueToMergeIn;
processTargetObj(obj1, function(){});
processTargetObj(obj1, function() {});
});
callback(err, objs);
}
@ -540,7 +537,7 @@ Inclusion.include = function (objects, include, options, cb) {
obj.__cachedRelations[relationName] = [];
}
filter.where[relation.keyTo] = {
inq: uniq(sourceIds)
inq: uniq(sourceIds),
};
relation.applyScope(null, filter);
options.partitionBy = relation.keyTo;
@ -575,7 +572,7 @@ Inclusion.include = function (objects, include, options, cb) {
function linkManyToOne(target, next) {
//fix for bug in hasMany with referencesMany
var targetIds = [].concat(target[relation.keyTo]);
async.each(targetIds, function (targetId, next) {
async.each(targetIds, function(targetId, next) {
var obj = objIdMap[targetId.toString()];
if (!obj) return next();
obj.__cachedRelations[relationName].push(target);
@ -643,13 +640,13 @@ Inclusion.include = function (objects, include, options, cb) {
utils.mergeQuery(typeFilter, filter);
var targetIds = targetIdsByType[modelType];
typeFilter.where[relation.keyTo] = {
inq: uniq(targetIds)
inq: uniq(targetIds),
};
var Model = lookupModel(relation.modelFrom.dataSource.modelBuilder.
models, modelType);
if (!Model) {
callback(new Error('Discriminator type "' + modelType +
' specified but no model exists with such name'));
callback(new Error(g.f('Discriminator type %s specified but no model exists with such name',
modelType)));
return;
}
relation.applyScope(null, typeFilter);
@ -679,7 +676,8 @@ Inclusion.include = function (objects, include, options, cb) {
async.each(targets, linkOneToMany, next);
function linkOneToMany(target, next) {
var objList = targetObjsMap[target[relation.keyTo].toString()];
async.each(objList, function (obj, next) {
if (!objList) return next();
async.each(objList, function(obj, next) {
if (!obj) return next();
obj.__cachedRelations[relationName] = target;
processTargetObj(obj, next);
@ -692,7 +690,6 @@ Inclusion.include = function (objects, include, options, cb) {
}
}
/**
* Handle Inclusion of Polymorphic HasOne relation
* @param callback
@ -715,7 +712,7 @@ Inclusion.include = function (objects, include, options, cb) {
obj.__cachedRelations[relationName] = null;
}
filter.where[relation.keyTo] = {
inq: uniq(sourceIds)
inq: uniq(sourceIds),
};
relation.applyScope(null, filter);
relation.modelTo.find(filter, options, targetFetchHandler);
@ -782,7 +779,7 @@ Inclusion.include = function (objects, include, options, cb) {
obj.__cachedRelations[relationName] = null;
}
filter.where[relation.keyTo] = {
inq: uniq(targetIds)
inq: uniq(targetIds),
};
relation.applyScope(null, filter);
relation.modelTo.find(filter, options, targetFetchHandler);
@ -810,7 +807,8 @@ Inclusion.include = function (objects, include, options, cb) {
function linkOneToMany(target, next) {
var targetId = target[relation.keyTo];
var objList = objTargetIdMap[targetId.toString()];
async.each(objList, function (obj, next) {
if (!objList) return next();
async.each(objList, function(obj, next) {
if (!obj) return next();
obj.__cachedRelations[relationName] = target;
processTargetObj(obj, next);
@ -822,7 +820,6 @@ Inclusion.include = function (objects, include, options, cb) {
}
}
/**
* Handle Inclusion of EmbedsMany/EmbedsManyWithBelongsTo/EmbedsOne
* Relations. Since Embedded docs are part of parents, no need to make
@ -833,7 +830,7 @@ Inclusion.include = function (objects, include, options, cb) {
* @param callback
*/
function includeEmbeds(callback) {
async.each(objs, function (obj, next) {
async.each(objs, function(obj, next) {
processTargetObj(obj, next);
}, callback);
}
@ -845,7 +842,6 @@ Inclusion.include = function (objects, include, options, cb) {
* @returns {*}
*/
function processTargetObj(obj, callback) {
var isInst = obj instanceof self;
// Calling the relation method on the instance
@ -916,11 +912,10 @@ Inclusion.include = function (objects, include, options, cb) {
related = inst[relationName].bind(inst, undefined);
}
related(options, function (err, result) {
related(options, function(err, result) {
if (err) {
return callback(err);
} else {
defineCachedRelations(obj);
obj.__cachedRelations[relationName] = result;
@ -928,7 +923,5 @@ Inclusion.include = function (objects, include, options, cb) {
}
});
}
}
};

View File

@ -1,3 +1,9 @@
// Copyright IBM Corp. 2015,2016. All Rights Reserved.
// Node module: loopback-datasource-juggler
// This file is licensed under the MIT License.
// License text available at https://opensource.org/licenses/MIT
'use strict';
module.exports.buildOneToOneIdentityMapWithOrigKeys = buildOneToOneIdentityMapWithOrigKeys;
module.exports.buildOneToManyIdentityMapWithOrigKeys = buildOneToManyIdentityMapWithOrigKeys;
module.exports.join = join;
@ -13,7 +19,7 @@ module.exports.KVMap = KVMap;
*/
function buildOneToOneIdentityMapWithOrigKeys(objs, idName) {
var kvMap = new KVMap();
for(var i = 0; i < objs.length; i++) {
for (var i = 0; i < objs.length; i++) {
var obj = objs[i];
var id = obj[idName];
kvMap.set(id, obj);
@ -23,7 +29,7 @@ function buildOneToOneIdentityMapWithOrigKeys(objs, idName) {
function buildOneToManyIdentityMapWithOrigKeys(objs, idName) {
var kvMap = new KVMap();
for(var i = 0; i < objs.length; i++) {
for (var i = 0; i < objs.length; i++) {
var obj = objs[i];
var id = obj[idName];
var value = kvMap.get(id) || [];
@ -33,7 +39,6 @@ function buildOneToManyIdentityMapWithOrigKeys(objs, idName) {
return kvMap;
}
/**
* Yeah, it joins. You need three things id -> obj1 map, id -> [obj2] map and merge function.
* This functions will take each obj1, locate all data to join in map2 and call merge function.
@ -43,7 +48,7 @@ function buildOneToManyIdentityMapWithOrigKeys(objs, idName) {
*/
function join(oneToOneIdMap, oneToManyIdMap, mergeF) {
var ids = oneToOneIdMap.getKeys();
for(var i = 0; i < ids.length; i++) {
for (var i = 0; i < ids.length; i++) {
var id = ids[i];
var obj = oneToOneIdMap.get(id);
var objectsToMergeIn = oneToManyIdMap.get(id) || [];
@ -51,34 +56,33 @@ function join(oneToOneIdMap, oneToManyIdMap, mergeF) {
}
}
/**
* Map with arbitrary keys and values. User .set() and .get() to work with values instead of []
* @returns {{set: Function, get: Function, remove: Function, exist: Function, getKeys: Function}}
* @constructor
*/
function KVMap(){
function KVMap() {
var _originalKeyFieldName = 'originalKey';
var _valueKeyFieldName = 'value';
var _dict = {};
var keyToString = function(key){ return key.toString() };
var keyToString = function(key) { return key.toString(); };
var mapImpl = {
set: function(key, value){
set: function(key, value) {
var recordObj = {};
recordObj[_originalKeyFieldName] = key;
recordObj[_valueKeyFieldName] = value;
_dict[keyToString(key)] = recordObj;
return true;
},
get: function(key){
get: function(key) {
var storeObj = _dict[keyToString(key)];
if(storeObj) {
if (storeObj) {
return storeObj[_valueKeyFieldName];
} else {
return undefined;
}
},
remove: function(key){
remove: function(key) {
delete _dict[keyToString(key)];
return true;
},
@ -86,13 +90,13 @@ function KVMap(){
var result = _dict.hasOwnProperty(keyToString(key));
return result;
},
getKeys: function(){
getKeys: function() {
var result = [];
for(var key in _dict) {
for (var key in _dict) {
result.push(_dict[key][_originalKeyFieldName]);
}
return result;
}
},
};
return mapImpl;

View File

@ -1,7 +1,11 @@
// Copyright IBM Corp. 2013,2016. All Rights Reserved.
// Node module: loopback-datasource-juggler
// This file is licensed under the MIT License.
// License text available at https://opensource.org/licenses/MIT
'use strict';
module.exports = function getIntrospector(ModelBuilder) {
function introspectType(value) {
// Unknown type, using Any
if (value === null || value === undefined) {
return ModelBuilder.Any;
@ -57,6 +61,4 @@ module.exports = function getIntrospector(ModelBuilder) {
ModelBuilder.introspect = introspectType;
return introspectType;
}
};

View File

@ -1,3 +1,9 @@
// Copyright IBM Corp. 2011,2016. All Rights Reserved.
// Node module: loopback-datasource-juggler
// This file is licensed under the MIT License.
// License text available at https://opensource.org/licenses/MIT
'use strict';
var util = require('util');
/**
@ -5,18 +11,18 @@ var util = require('util');
* @param newClass
* @param baseClass
*/
exports.inherits = function (newClass, baseClass, options) {
exports.inherits = function(newClass, baseClass, options) {
util.inherits(newClass, baseClass);
options = options || {
staticProperties: true,
override: false
override: false,
};
if (options.staticProperties) {
Object.keys(baseClass).forEach(function (classProp) {
if (classProp !== 'super_' && (!newClass.hasOwnProperty(classProp)
|| options.override)) {
Object.keys(baseClass).forEach(function(classProp) {
if (classProp !== 'super_' && (!newClass.hasOwnProperty(classProp) ||
options.override)) {
var pd = Object.getOwnPropertyDescriptor(baseClass, classProp);
Object.defineProperty(newClass, classProp, pd);
}
@ -30,7 +36,7 @@ exports.inherits = function (newClass, baseClass, options) {
* @param mixinClass The class to be mixed in
* @param options
*/
exports.mixin = function (newClass, mixinClass, options) {
exports.mixin = function(newClass, mixinClass, options) {
if (Array.isArray(newClass._mixins)) {
if (newClass._mixins.indexOf(mixinClass) !== -1) {
return;
@ -44,7 +50,7 @@ exports.mixin = function (newClass, mixinClass, options) {
staticProperties: true,
instanceProperties: true,
override: false,
proxyFunctions: false
proxyFunctions: false,
};
if (options.staticProperties === undefined) {
@ -67,7 +73,7 @@ exports.mixin = function (newClass, mixinClass, options) {
};
function mixInto(sourceScope, targetScope, options) {
Object.keys(sourceScope).forEach(function (propertyName) {
Object.keys(sourceScope).forEach(function(propertyName) {
var targetPropertyExists = targetScope.hasOwnProperty(propertyName);
var sourceProperty = Object.getOwnPropertyDescriptor(sourceScope, propertyName);
var targetProperty = targetPropertyExists && Object.getOwnPropertyDescriptor(targetScope, propertyName);

34
lib/kvao/expire.js Normal file
View File

@ -0,0 +1,34 @@
'use strict';
var assert = require('assert');
var utils = require('../utils');
/**
* Set the TTL (time to live) in ms (milliseconds) for a given key. TTL is the
* remaining time before a key-value pair is discarded from the database.
*
* @param {String} key Key to use when searching the database.
* @param {Number} ttl TTL in ms to set for the key.
* @options {Object} options
* @callback {Function} callback
* @param {Error} err Error object.
* @promise
*
* @header KVAO.expire(key, ttl, cb)
*/
module.exports = function keyValueExpire(key, ttl, options, callback) {
if (callback == undefined && typeof options === 'function') {
callback = options;
options = {};
} else if (!options) {
options = {};
}
assert(typeof key === 'string' && key, 'key must be a non-empty string');
assert(typeof ttl === 'number' && ttl > 0, 'ttl must be a positive integer');
assert(typeof options === 'object', 'options must be an object');
callback = callback || utils.createPromiseCallback();
this.getConnector().expire(this.modelName, key, ttl, options, callback);
return callback.promise;
};

34
lib/kvao/get.js Normal file
View File

@ -0,0 +1,34 @@
'use strict';
var assert = require('assert');
var utils = require('../utils');
/**
* Return the value associated with a given key.
*
* @param {String} key Key to use when searching the database.
* @options {Object} options
* @callback {Function} callback
* @param {Error} err Error object.
* @param {*} result Value associated with the given key.
* @promise
*
* @header KVAO.get(key, cb)
*/
module.exports = function keyValueGet(key, options, callback) {
if (callback == undefined && typeof options === 'function') {
callback = options;
options = {};
} else if (!options) {
options = {};
}
assert(typeof key === 'string' && key, 'key must be a non-empty string');
callback = callback || utils.createPromiseCallback();
this.getConnector().get(this.modelName, key, options, function(err, result) {
// TODO convert raw result to Model instance (?)
callback(err, result);
});
return callback.promise;
};

18
lib/kvao/index.js Normal file
View File

@ -0,0 +1,18 @@
'use strict';
function KeyValueAccessObject() {
};
module.exports = KeyValueAccessObject;
KeyValueAccessObject.get = require('./get');
KeyValueAccessObject.set = require('./set');
KeyValueAccessObject.expire = require('./expire');
KeyValueAccessObject.ttl = require('./ttl');
KeyValueAccessObject.iterateKeys = require('./iterate-keys');
KeyValueAccessObject.keys = require('./keys');
KeyValueAccessObject.getConnector = function() {
return this.getDataSource().connector;
};

38
lib/kvao/iterate-keys.js Normal file
View File

@ -0,0 +1,38 @@
'use strict';
var assert = require('assert');
var utils = require('../utils');
/**
* Asynchronously iterate all keys in the database. Similar to `.keys()` but
* instead allows for iteration over large data sets without having to load
* everything into memory at once.
*
* @param {Object} filter An optional filter object with the following
* @param {String} filter.match Glob string to use to filter returned
* keys (i.e. `userid.*`). All connectors are required to support `*` and
* `?`. They may also support additional special characters that are
* specific to the backing database.
* @param {Object} options
* @returns {AsyncIterator} An Object implementing `next(cb) -> Promise`
* function that can be used to iterate all keys.
*
* @header KVAO.iterateKeys(filter)
*/
module.exports = function keyValueIterateKeys(filter, options) {
filter = filter || {};
options = options || {};
assert(typeof filter === 'object', 'filter must be an object');
assert(typeof options === 'object', 'options must be an object');
var iter = this.getConnector().iterateKeys(this.modelName, filter, options);
// promisify the returned iterator
return {
next: function(callback) {
callback = callback || utils.createPromiseCallback();
iter.next(callback);
return callback.promise;
},
};
};

56
lib/kvao/keys.js Normal file
View File

@ -0,0 +1,56 @@
'use strict';
var assert = require('assert');
var utils = require('../utils');
/**
* Return all keys in the database.
*
* **WARNING**: This method is not suitable for large data sets as all
* key-values pairs are loaded into memory at once. For large data sets,
* use `iterateKeys()` instead.
*
* @param {Object} filter An optional filter object with the following
* @param {String} filter.match Glob string used to filter returned
* keys (i.e. `userid.*`). All connectors are required to support `*` and
* `?`, but may also support additional special characters specific to the
* database.
* @param {Object} options
* @callback {Function} callback
* @promise
*
*
* @header KVAO.keys(filter, callback)
*/
module.exports = function keyValueKeys(filter, options, callback) {
if (callback === undefined) {
if (typeof options === 'function') {
callback = options;
options = undefined;
} else if (options === undefined && typeof filter === 'function') {
callback = filter;
filter = undefined;
}
}
filter = filter || {};
options = options || {};
assert(typeof filter === 'object', 'filter must be an object');
assert(typeof options === 'object', 'options must be an object');
callback = callback || utils.createPromiseCallback();
var iter = this.iterateKeys(filter, options);
var keys = [];
iter.next(onNextKey);
function onNextKey(err, key) {
if (err) return callback(err);
if (key === undefined) return callback(null, keys);
keys.push(key);
iter.next(onNextKey);
}
return callback.promise;
};

44
lib/kvao/set.js Normal file
View File

@ -0,0 +1,44 @@
'use strict';
var assert = require('assert');
var utils = require('../utils');
/**
* Persist a value and associate it with the given key.
*
* @param {String} key Key to associate with the given value.
* @param {*} value Value to persist.
* @options {Number|Object} options Optional settings for the key-value
* pair. If a Number is provided, it is set as the TTL (time to live) in ms
* (milliseconds) for the key-value pair.
* @property {Number} ttl TTL for the key-value pair in ms.
* @callback {Function} callback
* @param {Error} err Error object.
* @promise
*
* @header KVAO.set(key, value, cb)
*/
module.exports = function keyValueSet(key, value, options, callback) {
if (callback == undefined && typeof options === 'function') {
callback = options;
options = {};
} else if (typeof options === 'number') {
options = {ttl: options};
} else if (!options) {
options = {};
}
assert(typeof key === 'string' && key, 'key must be a non-empty string');
assert(value != null, 'value must be defined and not null');
assert(typeof options === 'object', 'options must be an object');
if (options && 'ttl' in options) {
assert(typeof options.ttl === 'number' && options.ttl > 0,
'options.ttl must be a positive number');
}
callback = callback || utils.createPromiseCallback();
// TODO convert possible model instance in "value" to raw data via toObect()
this.getConnector().set(this.modelName, key, value, options, callback);
return callback.promise;
};

34
lib/kvao/ttl.js Normal file
View File

@ -0,0 +1,34 @@
'use strict';
var assert = require('assert');
var utils = require('../utils');
/**
* Return the TTL (time to live) for a given key. TTL is the remaining time
* before a key-value pair is discarded from the database.
*
* @param {String} key Key to use when searching the database.
* @options {Object} options
* @callback {Function} callback
* @param {Error} error
* @param {Number} ttl Expiration time for the key-value pair. `undefined` if
* TTL was not initially set.
* @promise
*
* @header KVAO.ttl(key, cb)
*/
module.exports = function keyValueTtl(key, options, callback) {
if (callback == undefined && typeof options === 'function') {
callback = options;
options = {};
} else if (!options) {
options = {};
}
assert(typeof key === 'string' && key, 'key must be a non-empty string');
assert(typeof options === 'object', 'options must be an object');
callback = callback || utils.createPromiseCallback();
this.getConnector().ttl(this.modelName, key, options, callback);
return callback.promise;
};

View File

@ -1,3 +1,10 @@
// Copyright IBM Corp. 2012,2016. All Rights Reserved.
// Node module: loopback-datasource-juggler
// This file is licensed under the MIT License.
// License text available at https://opensource.org/licenses/MIT
'use strict';
var g = require('strong-globalize')();
var util = require('util');
var Any = require('./types').Types.Any;
@ -13,7 +20,7 @@ function List(items, itemType, parent) {
try {
items = JSON.parse(items);
} catch (e) {
var err = new Error(util.format('could not create List from JSON string: %j', items));
var err = new Error(g.f('could not create List from JSON string: %j', items));
err.statusCode = 400;
throw err;
}
@ -24,12 +31,12 @@ function List(items, itemType, parent) {
items = items || [];
if (!Array.isArray(items)) {
var err = new Error(util.format('Items must be an array: %j', items));
var err = new Error(g.f('Items must be an array: %j', items));
err.statusCode = 400;
throw err;
}
if(!itemType) {
if (!itemType) {
itemType = items[0] && items[0].constructor;
}
@ -37,25 +44,25 @@ function List(items, itemType, parent) {
itemType = itemType[0];
}
if(itemType === Array) {
if (itemType === Array) {
itemType = Any;
}
Object.defineProperty(arr, 'itemType', {
writable: true,
enumerable: false,
value: itemType
value: itemType,
});
if (parent) {
Object.defineProperty(arr, 'parent', {
writable: true,
enumerable: false,
value: parent
value: parent,
});
}
items.forEach(function (item, i) {
items.forEach(function(item, i) {
if (itemType && !(item instanceof itemType)) {
arr[i] = itemType(item);
} else {
@ -70,15 +77,15 @@ util.inherits(List, Array);
var _push = List.prototype.push;
List.prototype.push = function (obj) {
List.prototype.push = function(obj) {
var item = this.itemType && (obj instanceof this.itemType) ? obj : this.itemType(obj);
_push.call(this, item);
return item;
};
List.prototype.toObject = function (onlySchema, removeHidden, removeProtected) {
List.prototype.toObject = function(onlySchema, removeHidden, removeProtected) {
var items = [];
this.forEach(function (item) {
this.forEach(function(item) {
if (item && typeof item === 'object' && item.toObject) {
items.push(item.toObject(onlySchema, removeHidden, removeProtected));
} else {
@ -88,11 +95,11 @@ List.prototype.toObject = function (onlySchema, removeHidden, removeProtected) {
return items;
};
List.prototype.toJSON = function () {
List.prototype.toJSON = function() {
return this.toObject(true);
};
List.prototype.toString = function () {
List.prototype.toString = function() {
return JSON.stringify(this.toJSON());
};

View File

@ -1,3 +1,9 @@
// Copyright IBM Corp. 2014,2016. All Rights Reserved.
// Node module: loopback-datasource-juggler
// This file is licensed under the MIT License.
// License text available at https://opensource.org/licenses/MIT
'use strict';
var debug = require('debug')('loopback:mixin');
var assert = require('assert');
var DefaultModelBaseClass = require('./model.js');
@ -35,7 +41,7 @@ MixinProvider.prototype.applyMixin = function applyMixin(modelClass, name, optio
} else {
// Try model name
var model = this.modelBuilder.getModel(name);
if(model) {
if (model) {
debug('Mixin is resolved to a model: %s', name);
modelClass.mixin(model, options);
} else {
@ -57,7 +63,7 @@ MixinProvider.prototype.define = function defineMixin(name, mixin) {
debug('Defining mixin: %s', name);
}
if (isModelClass(mixin)) {
this.mixins[name] = function (Model, options) {
this.mixins[name] = function(Model, options) {
Model.mixin(mixin, options);
};
} else if (typeof mixin === 'function') {

View File

@ -1,7 +1,14 @@
// Copyright IBM Corp. 2013,2016. All Rights Reserved.
// Node module: loopback-datasource-juggler
// This file is licensed under the MIT License.
// License text available at https://opensource.org/licenses/MIT
'use strict';
/*!
* Module dependencies
*/
var g = require('strong-globalize')();
var inflection = require('inflection');
var EventEmitter = require('events').EventEmitter;
var util = require('util');
@ -39,6 +46,7 @@ function ModelBuilder() {
// create blank models pool
this.models = {};
this.definitions = {};
this.settings = {};
this.mixins = new MixinProvider(this);
this.defaultModelBaseClass = DefaultModelBaseClass;
}
@ -63,7 +71,7 @@ function isModelClass(cls) {
* @param {Boolean} forceCreate Whether the create a stub for the given name if a model doesn't exist.
* @returns {*} The model class
*/
ModelBuilder.prototype.getModel = function (name, forceCreate) {
ModelBuilder.prototype.getModel = function(name, forceCreate) {
var model = this.models[name];
if (!model && forceCreate) {
model = this.define(name, {}, {unresolved: true});
@ -76,7 +84,7 @@ ModelBuilder.prototype.getModel = function (name, forceCreate) {
* @param {String} name The model name
* @returns {ModelDefinition} The model definition
*/
ModelBuilder.prototype.getModelDefinition = function (name) {
ModelBuilder.prototype.getModelDefinition = function(name) {
return this.definitions[name];
};
@ -119,7 +127,7 @@ ModelBuilder.prototype.define = function defineClass(className, properties, sett
var pathName = httpOptions.path || pluralName;
if (!className) {
throw new Error('Class name required');
throw new Error(g.f('Class name required'));
}
if (args.length === 1) {
properties = {};
@ -172,7 +180,7 @@ ModelBuilder.prototype.define = function defineClass(className, properties, sett
return new ModelConstructor(data, options);
}
if (ModelClass.settings.unresolved) {
throw new Error('Model ' + ModelClass.modelName + ' is not defined.');
throw new Error(g.f('Model %s is not defined.', ModelClass.modelName));
}
ModelBaseClass.apply(this, arguments);
};
@ -222,9 +230,10 @@ ModelBuilder.prototype.define = function defineClass(className, properties, sett
// Support both flavors path: 'x' and path: '/x'
pathName = '/' + pathName;
}
hiddenProperty(ModelClass, 'http', { path: pathName });
hiddenProperty(ModelClass, 'http', {path: pathName});
hiddenProperty(ModelClass, 'base', ModelBaseClass);
hiddenProperty(ModelClass, '_observers', {});
hiddenProperty(ModelClass, '_warned', {});
// inherit ModelBaseClass static methods
for (var i in ModelBaseClass) {
@ -236,7 +245,7 @@ ModelBuilder.prototype.define = function defineClass(className, properties, sett
// Load and inject the model classes
if (settings.models) {
Object.keys(settings.models).forEach(function (m) {
Object.keys(settings.models).forEach(function(m) {
var model = settings.models[m];
ModelClass[m] = typeof model === 'string' ? modelBuilder.getModel(model, true) : model;
});
@ -254,8 +263,8 @@ ModelBuilder.prototype.define = function defineClass(className, properties, sett
// Warn about properties with unsupported names
if (/\./.test(p)) {
deprecated('Property names containing a dot are not supported. ' +
'Model: ' + className + ', property: ' + p);
deprecated(g.f('Property names containing a dot are not supported. ' +
'Model: %s, property: %s', className, p));
}
}
@ -283,7 +292,7 @@ ModelBuilder.prototype.define = function defineClass(className, properties, sett
// Add the id property
if (idInjection) {
// Set up the id property
ModelClass.definition.defineProperty('id', { type: Number, id: 1, generated: true });
ModelClass.definition.defineProperty('id', {type: Number, id: 1, generated: true});
}
idNames = modelDefinition.idNames(); // Reload it after rebuild
@ -292,33 +301,33 @@ ModelBuilder.prototype.define = function defineClass(className, properties, sett
var idProp = idNames[0];
if (idProp !== 'id') {
Object.defineProperty(ModelClass.prototype, 'id', {
get: function () {
get: function() {
var idProp = ModelClass.definition.idNames()[0];
return this.__data[idProp];
return this.__data && this.__data[idProp];
},
configurable: true,
enumerable: false
enumerable: false,
});
}
} else {
// Now the id property is an object that consists of multiple keys
Object.defineProperty(ModelClass.prototype, 'id', {
get: function () {
get: function() {
var compositeId = {};
var idNames = ModelClass.definition.idNames();
for (var i = 0, p; i < idNames.length; i++) {
p = idNames[i];
compositeId[p] = this.__data[p];
compositeId[p] = this.__data && this.__data[p];
}
return compositeId;
},
configurable: true,
enumerable: false
enumerable: false,
});
}
// A function to loop through the properties
ModelClass.forEachProperty = function (cb) {
ModelClass.forEachProperty = function(cb) {
var props = ModelClass.definition.properties;
var keys = Object.keys(props);
for (var i = 0, n = keys.length; i < n; i++) {
@ -327,7 +336,7 @@ ModelBuilder.prototype.define = function defineClass(className, properties, sett
};
// A function to attach the model class to a data source
ModelClass.attachTo = function (dataSource) {
ModelClass.attachTo = function(dataSource) {
dataSource.attach(this);
};
@ -351,7 +360,7 @@ ModelBuilder.prototype.define = function defineClass(className, properties, sett
* @options {Object} settings Model settings, such as relations and acls.
*
*/
ModelClass.extend = function (className, subclassProperties, subclassSettings) {
ModelClass.extend = function(className, subclassProperties, subclassSettings) {
var properties = ModelClass.definition.properties;
var settings = ModelClass.definition.settings;
@ -413,12 +422,12 @@ ModelBuilder.prototype.define = function defineClass(className, properties, sett
* Register a property for the model class
* @param {String} propertyName Name of the property.
*/
ModelClass.registerProperty = function (propertyName) {
ModelClass.registerProperty = function(propertyName) {
var properties = modelDefinition.build();
var prop = properties[propertyName];
var DataType = prop.type;
if (!DataType) {
throw new Error('Invalid type for property ' + propertyName);
throw new Error(g.f('Invalid type for property %s', propertyName));
}
if (prop.required) {
@ -427,14 +436,14 @@ ModelBuilder.prototype.define = function defineClass(className, properties, sett
}
Object.defineProperty(ModelClass.prototype, propertyName, {
get: function () {
get: function() {
if (ModelClass.getter[propertyName]) {
return ModelClass.getter[propertyName].call(this); // Try getter first
} else {
return this.__data && this.__data[propertyName]; // Try __data
}
},
set: function (value) {
set: function(value) {
var DataType = ModelClass.definition.properties[propertyName].type;
if (Array.isArray(DataType) || DataType === Array) {
DataType = List;
@ -469,23 +478,23 @@ ModelBuilder.prototype.define = function defineClass(className, properties, sett
}
},
configurable: true,
enumerable: true
enumerable: true,
});
// FIXME: [rfeng] Do we need to keep the raw data?
// Use $ as the prefix to avoid conflicts with properties such as _id
Object.defineProperty(ModelClass.prototype, '$' + propertyName, {
get: function () {
get: function() {
return this.__data && this.__data[propertyName];
},
set: function (value) {
set: function(value) {
if (!this.__data) {
this.__data = {};
}
this.__data[propertyName] = value;
},
configurable: true,
enumerable: false
enumerable: false,
});
};
@ -521,14 +530,13 @@ ModelBuilder.prototype.define = function defineClass(className, properties, sett
ModelClass.emit('defined', ModelClass);
return ModelClass;
};
// DataType for Date
function DateType(arg) {
var d = new Date(arg);
if (isNaN(d.getTime())) {
throw new Error('Invalid date: ' + arg);
throw new Error(g.f('Invalid date: %s', arg));
}
return d;
}
@ -558,7 +566,7 @@ function BooleanType(arg) {
* @param {String} propertyName Name of property
* @param {Object} propertyDefinition Property settings
*/
ModelBuilder.prototype.defineProperty = function (model, propertyName, propertyDefinition) {
ModelBuilder.prototype.defineProperty = function(model, propertyName, propertyDefinition) {
this.definitions[model].defineProperty(propertyName, propertyDefinition);
this.models[model].registerProperty(propertyName);
};
@ -602,7 +610,7 @@ ModelBuilder.prototype.defineValueType = function(type, aliases) {
* @property {String} type Datatype of property: Must be an [LDL type](http://docs.strongloop.com/display/LB/LoopBack+types).
* @property {Boolean} index True if the property is an index; false otherwise.
*/
ModelBuilder.prototype.extendModel = function (model, props) {
ModelBuilder.prototype.extendModel = function(model, props) {
var t = this;
var keys = Object.keys(props);
for (var i = 0; i < keys.length; i++) {
@ -628,12 +636,11 @@ ModelBuilder.prototype.copyModel = function copyModel(Master) {
hiddenProperty(Slave, 'relations', Master.relations);
if (!(className in modelBuilder.models)) {
// store class in model pool
modelBuilder.models[className] = Slave;
modelBuilder.definitions[className] = {
properties: md.properties,
settings: md.settings
settings: md.settings,
};
}
@ -648,14 +655,14 @@ function hiddenProperty(where, property, value) {
writable: true,
enumerable: false,
configurable: true,
value: value
value: value,
});
}
/**
* Get the schema name
*/
ModelBuilder.prototype.getSchemaName = function (name) {
ModelBuilder.prototype.getSchemaName = function(name) {
if (name) {
return name;
}
@ -672,7 +679,7 @@ ModelBuilder.prototype.getSchemaName = function (name) {
* Returns {Function} if the type is resolved
* @param {String} type The type string, such as 'number', 'Number', 'boolean', or 'String'. It's case insensitive
*/
ModelBuilder.prototype.resolveType = function (type) {
ModelBuilder.prototype.resolveType = function(type) {
if (!type) {
return type;
}
@ -681,8 +688,7 @@ ModelBuilder.prototype.resolveType = function (type) {
var itemType = this.resolveType(type[0]);
if (typeof itemType === 'function') {
return [itemType];
}
else {
} else {
return itemType; // Not resolved, return the type string
}
}
@ -701,7 +707,11 @@ ModelBuilder.prototype.resolveType = function (type) {
return this.resolveType(type.type);
} else {
return this.define(this.getSchemaName(null),
type, {anonymous: true, idInjection: false});
type, {
anonymous: true,
idInjection: false,
strict: this.settings.strictEmbeddedModels || false,
});
}
} else if ('function' === typeof type) {
return type;
@ -721,7 +731,7 @@ ModelBuilder.prototype.resolveType = function (type) {
* @param {*} schemas The schemas
* @returns {Object} A map of model constructors keyed by model name
*/
ModelBuilder.prototype.buildModels = function (schemas, createModel) {
ModelBuilder.prototype.buildModels = function(schemas, createModel) {
var models = {};
// Normalize the schemas to be an array of the schema objects {name: <name>, properties: {}, options: {}}
@ -735,8 +745,8 @@ ModelBuilder.prototype.buildModels = function (schemas, createModel) {
{
name: this.getSchemaName(),
properties: schemas,
options: {anonymous: true}
}
options: {anonymous: true},
},
];
}
}
@ -746,7 +756,7 @@ ModelBuilder.prototype.buildModels = function (schemas, createModel) {
var name = this.getSchemaName(schemas[s].name);
schemas[s].name = name;
var model;
if(typeof createModel === 'function') {
if (typeof createModel === 'function') {
model = createModel(schemas[s].name, schemas[s].properties, schemas[s].options);
} else {
model = this.define(schemas[s].name, schemas[s].properties, schemas[s].options);
@ -776,14 +786,10 @@ ModelBuilder.prototype.buildModels = function (schemas, createModel) {
* @param {Object} options The options
* @returns {}
*/
ModelBuilder.prototype.buildModelFromInstance = function (name, json, options) {
ModelBuilder.prototype.buildModelFromInstance = function(name, json, options) {
// Introspect the JSON document to generate a schema
var schema = introspect(json);
// Create a model for the generated schema
return this.define(name, schema, options);
};

View File

@ -1,3 +1,9 @@
// Copyright IBM Corp. 2013,2016. All Rights Reserved.
// Node module: loopback-datasource-juggler
// This file is licensed under the MIT License.
// License text available at https://opensource.org/licenses/MIT
'use strict';
var assert = require('assert');
var util = require('util');
var EventEmitter = require('events').EventEmitter;
@ -53,7 +59,7 @@ require('./types')(ModelDefinition);
* Return table name for specified `modelName`
* @param {String} connectorType The connector type, such as 'oracle' or 'mongodb'
*/
ModelDefinition.prototype.tableName = function (connectorType) {
ModelDefinition.prototype.tableName = function(connectorType) {
var settings = this.settings;
if (settings[connectorType]) {
return settings[connectorType].table || settings[connectorType].tableName || this.name;
@ -68,7 +74,7 @@ ModelDefinition.prototype.tableName = function (connectorType) {
* @param propertyName The property name
* @returns {String} columnName
*/
ModelDefinition.prototype.columnName = function (connectorType, propertyName) {
ModelDefinition.prototype.columnName = function(connectorType, propertyName) {
if (!propertyName) {
return propertyName;
}
@ -87,7 +93,7 @@ ModelDefinition.prototype.columnName = function (connectorType, propertyName) {
* @param propertyName The property name
* @returns {Object} column metadata
*/
ModelDefinition.prototype.columnMetadata = function (connectorType, propertyName) {
ModelDefinition.prototype.columnMetadata = function(connectorType, propertyName) {
if (!propertyName) {
return propertyName;
}
@ -105,7 +111,7 @@ ModelDefinition.prototype.columnMetadata = function (connectorType, propertyName
* @param {String} connectorType The connector type, such as 'oracle' or 'mongodb'
* @returns {String[]} column names
*/
ModelDefinition.prototype.columnNames = function (connectorType) {
ModelDefinition.prototype.columnNames = function(connectorType) {
this.build();
var props = this.properties;
var cols = [];
@ -123,7 +129,7 @@ ModelDefinition.prototype.columnNames = function (connectorType) {
* Find the ID properties sorted by the index
* @returns {Object[]} property name/index for IDs
*/
ModelDefinition.prototype.ids = function () {
ModelDefinition.prototype.ids = function() {
if (this._ids) {
return this._ids;
}
@ -140,7 +146,7 @@ ModelDefinition.prototype.ids = function () {
}
ids.push({name: key, id: id, property: props[key]});
}
ids.sort(function (a, b) {
ids.sort(function(a, b) {
return a.id - b.id;
});
this._ids = ids;
@ -152,7 +158,7 @@ ModelDefinition.prototype.ids = function () {
* @param {String} modelName The model name
* @returns {String} columnName for ID
*/
ModelDefinition.prototype.idColumnName = function (connectorType) {
ModelDefinition.prototype.idColumnName = function(connectorType) {
return this.columnName(connectorType, this.idName());
};
@ -160,7 +166,7 @@ ModelDefinition.prototype.idColumnName = function (connectorType) {
* Find the ID property name
* @returns {String} property name for ID
*/
ModelDefinition.prototype.idName = function () {
ModelDefinition.prototype.idName = function() {
var id = this.ids()[0];
if (this.properties.id && this.properties.id.id) {
return 'id';
@ -173,9 +179,9 @@ ModelDefinition.prototype.idName = function () {
* Find the ID property names sorted by the index
* @returns {String[]} property names for IDs
*/
ModelDefinition.prototype.idNames = function () {
ModelDefinition.prototype.idNames = function() {
var ids = this.ids();
var names = ids.map(function (id) {
var names = ids.map(function(id) {
return id.name;
});
return names;
@ -185,7 +191,7 @@ ModelDefinition.prototype.idNames = function () {
*
* @returns {{}}
*/
ModelDefinition.prototype.indexes = function () {
ModelDefinition.prototype.indexes = function() {
this.build();
var indexes = {};
if (this.settings.indexes) {
@ -205,7 +211,7 @@ ModelDefinition.prototype.indexes = function () {
* Build a model definition
* @param {Boolean} force Forcing rebuild
*/
ModelDefinition.prototype.build = function (forceRebuild) {
ModelDefinition.prototype.build = function(forceRebuild) {
if (forceRebuild) {
this.properties = null;
this.relations = [];
@ -224,11 +230,11 @@ ModelDefinition.prototype.build = function (forceRebuild) {
source: this.name,
target: type,
type: Array.isArray(prop) ? 'hasMany' : 'belongsTo',
as: p
as: p,
});
} else {
var typeDef = {
type: type
type: type,
};
if (typeof prop === 'object' && prop !== null) {
for (var a in prop) {
@ -249,7 +255,7 @@ ModelDefinition.prototype.build = function (forceRebuild) {
* @param {String} propertyName The property name
* @param {Object} propertyDefinition The property definition
*/
ModelDefinition.prototype.defineProperty = function (propertyName, propertyDefinition) {
ModelDefinition.prototype.defineProperty = function(propertyName, propertyDefinition) {
this.rawProperties[propertyName] = propertyDefinition;
this.build(true);
};
@ -261,7 +267,7 @@ function isModelClass(cls) {
return cls.prototype instanceof ModelBaseClass;
}
ModelDefinition.prototype.toJSON = function (forceRebuild) {
ModelDefinition.prototype.toJSON = function(forceRebuild) {
if (forceRebuild) {
this.json = null;
}
@ -271,11 +277,11 @@ ModelDefinition.prototype.toJSON = function (forceRebuild) {
var json = {
name: this.name,
properties: {},
settings: this.settings
settings: this.settings,
};
this.build(forceRebuild);
var mapper = function (val) {
var mapper = function(val) {
if (val === undefined || val === null) {
return val;
}

View File

@ -1,3 +1,12 @@
// Copyright IBM Corp. 2013,2016. All Rights Reserved.
// Node module: loopback-datasource-juggler
// This file is licensed under the MIT License.
// License text available at https://opensource.org/licenses/MIT
// Turning on strict for this file breaks lots of test cases;
// disabling strict for this file
/* eslint-disable strict */
/*!
* Module exports class Model
*/
@ -7,7 +16,7 @@ module.exports = ModelBaseClass;
* Module dependencies
*/
var async = require('async');
var g = require('strong-globalize')();
var util = require('util');
var jutil = require('./jutil');
var List = require('./list');
@ -16,8 +25,9 @@ var validations = require('./validations');
var _extend = util._extend;
var utils = require('./utils');
var fieldsToArray = utils.fieldsToArray;
var uuid = require('node-uuid');
var uuid = require('uuid');
var deprecated = require('depd')('loopback-datasource-juggler');
var shortid = require('shortid');
// Set up an object for quick lookup
var BASE_TYPES = {
@ -26,7 +36,7 @@ var BASE_TYPES = {
'Number': true,
'Date': true,
'Text': true,
'ObjectID': true
'ObjectID': true,
};
/**
@ -39,7 +49,7 @@ var BASE_TYPES = {
*/
function ModelBaseClass(data, options) {
options = options || {};
if(!('applySetters' in options)) {
if (!('applySetters' in options)) {
// Default to true
options.applySetters = true;
}
@ -59,11 +69,11 @@ function ModelBaseClass(data, options) {
* @property {Boolean} persisted Whether the instance has been persisted
* @private
*/
ModelBaseClass.prototype._initProperties = function (data, options) {
ModelBaseClass.prototype._initProperties = function(data, options) {
var self = this;
var ctor = this.constructor;
if(data instanceof ctor) {
if (data instanceof ctor) {
// Convert the data to be plain object to avoid pollutions
data = data.toObject(false);
}
@ -79,7 +89,7 @@ ModelBaseClass.prototype._initProperties = function (data, options) {
var applyDefaultValues = options.applyDefaultValues;
var strict = options.strict;
if(strict === undefined) {
if (strict === undefined) {
strict = ctor.definition.settings.strict;
}
@ -94,14 +104,14 @@ ModelBaseClass.prototype._initProperties = function (data, options) {
writable: true,
enumerable: false,
configurable: true,
value: {}
value: {},
},
__data: {
writable: true,
enumerable: false,
configurable: true,
value: {}
value: {},
},
// Instance level data source
@ -109,7 +119,7 @@ ModelBaseClass.prototype._initProperties = function (data, options) {
writable: true,
enumerable: false,
configurable: true,
value: options.dataSource
value: options.dataSource,
},
// Instance level strict mode
@ -117,14 +127,14 @@ ModelBaseClass.prototype._initProperties = function (data, options) {
writable: true,
enumerable: false,
configurable: true,
value: strict
value: strict,
},
__persisted: {
writable: true,
enumerable: false,
configurable: true,
value: false
value: false,
},
});
@ -133,7 +143,7 @@ ModelBaseClass.prototype._initProperties = function (data, options) {
writable: true,
enumerable: false,
configrable: true,
value: []
value: [],
});
}
} else {
@ -192,7 +202,7 @@ ModelBaseClass.prototype._initProperties = function (data, options) {
var multiple = ctor.relations[p].multiple;
var typeName = multiple ? 'Array' : modelTo.modelName;
var propType = multiple ? [modelTo] : modelTo;
properties[p] = { name: typeName, type: propType };
properties[p] = {name: typeName, type: propType};
this.setStrict(false);
}
@ -207,7 +217,11 @@ ModelBaseClass.prototype._initProperties = function (data, options) {
if (!~fields.indexOf(ctor.relations[p].keyTo)) {
fields.push(ctor.relations[p].keyTo);
}
self.__data[p] = new modelTo(propVal, { fields: fields, applySetters: false, persisted: options.persisted });
self.__data[p] = new modelTo(propVal, {
fields: fields,
applySetters: false,
persisted: options.persisted,
});
}
}
@ -220,12 +234,11 @@ ModelBaseClass.prototype._initProperties = function (data, options) {
// Warn about properties with unsupported names
if (/\./.test(p)) {
deprecated('Property names containing a dot are not supported. ' +
'Model: ' + this.constructor.modelName +
', dynamic property: ' + p);
deprecated(g.f('Property names containing a dot are not supported. ' +
'Model: %s, dynamic property: %s', this.constructor.modelName, p));
}
} else if (strict === 'throw') {
throw new Error('Unknown property: ' + p);
throw new Error(g.f('Unknown property: %s', p));
} else if (strict === 'validate') {
this.__unknownProperties.push(p);
}
@ -287,9 +300,12 @@ ModelBaseClass.prototype._initProperties = function (data, options) {
case 'now':
propVal = new Date();
break;
case 'shortid':
propVal = shortid.generate();
break;
default:
// TODO Support user-provided functions via a registry of functions
console.warn('Unknown default value provider ' + defn);
g.warn('Unknown default value provider %s', defn);
}
// FIXME: We should coerce the value
// will implement it after we refactor the PropertyDefinition
@ -303,7 +319,6 @@ ModelBaseClass.prototype._initProperties = function (data, options) {
// Handle complex types (JSON/Object)
if (!BASE_TYPES[type.name]) {
if (typeof self.__data[p] !== 'object' && self.__data[p]) {
try {
self.__data[p] = JSON.parse(self.__data[p] + '');
@ -313,15 +328,15 @@ ModelBaseClass.prototype._initProperties = function (data, options) {
}
if (type.prototype instanceof ModelBaseClass) {
if (!(self.__data[p] instanceof type)
&& typeof self.__data[p] === 'object'
&& self.__data[p] !== null ) {
if (!(self.__data[p] instanceof type) &&
typeof self.__data[p] === 'object' &&
self.__data[p] !== null) {
self.__data[p] = new type(self.__data[p]);
}
} else if (type.name === 'Array' || Array.isArray(type)) {
if (!(self.__data[p] instanceof List)
&& self.__data[p] !== undefined
&& self.__data[p] !== null ) {
if (!(self.__data[p] instanceof List) &&
self.__data[p] !== undefined &&
self.__data[p] !== null) {
self.__data[p] = List(self.__data[p], type, self);
}
}
@ -335,28 +350,28 @@ ModelBaseClass.prototype._initProperties = function (data, options) {
* @param {String} prop Property name
* @param {Object} params Various property configuration
*/
ModelBaseClass.defineProperty = function (prop, params) {
if(this.dataSource) {
ModelBaseClass.defineProperty = function(prop, params) {
if (this.dataSource) {
this.dataSource.defineProperty(this.modelName, prop, params);
} else {
this.modelBuilder.defineProperty(this.modelName, prop, params);
}
};
ModelBaseClass.getPropertyType = function (propName) {
ModelBaseClass.getPropertyType = function(propName) {
var prop = this.definition.properties[propName];
if (!prop) {
// The property is not part of the definition
return null;
}
if (!prop.type) {
throw new Error('Type not defined for property ' + this.modelName + '.' + propName);
throw new Error(g.f('Type not defined for property %s.%s', this.modelName, propName));
// return null;
}
return prop.type.name;
};
ModelBaseClass.prototype.getPropertyType = function (propName) {
ModelBaseClass.prototype.getPropertyType = function(propName) {
return this.constructor.getPropertyType(propName);
};
@ -364,7 +379,7 @@ ModelBaseClass.prototype.getPropertyType = function (propName) {
* Return string representation of class
* This overrides the default `toString()` method
*/
ModelBaseClass.toString = function () {
ModelBaseClass.toString = function() {
return '[Model ' + this.modelName + ']';
};
@ -374,7 +389,7 @@ ModelBaseClass.toString = function () {
*
* @param {Boolean} onlySchema Restrict properties to dataSource only. Default is false. If true, the function returns only properties defined in the schema; Otherwise it returns all enumerable properties.
*/
ModelBaseClass.prototype.toObject = function (onlySchema, removeHidden, removeProtected) {
ModelBaseClass.prototype.toObject = function(onlySchema, removeHidden, removeProtected) {
if (onlySchema === undefined) {
onlySchema = true;
}
@ -499,7 +514,7 @@ ModelBaseClass.prototype.toObject = function (onlySchema, removeHidden, removePr
return data;
};
ModelBaseClass.isProtectedProperty = function (propertyName) {
ModelBaseClass.isProtectedProperty = function(propertyName) {
var Model = this;
var settings = Model.definition && Model.definition.settings;
var protectedProperties = settings && (settings.protectedProperties || settings.protected);
@ -518,7 +533,7 @@ ModelBaseClass.isProtectedProperty = function (propertyName) {
}
};
ModelBaseClass.isHiddenProperty = function (propertyName) {
ModelBaseClass.isHiddenProperty = function(propertyName) {
var Model = this;
var settings = Model.definition && Model.definition.settings;
var hiddenProperties = settings && (settings.hiddenProperties || settings.hidden);
@ -537,11 +552,11 @@ ModelBaseClass.isHiddenProperty = function (propertyName) {
}
};
ModelBaseClass.prototype.toJSON = function () {
ModelBaseClass.prototype.toJSON = function() {
return this.toObject(false, true, false);
};
ModelBaseClass.prototype.fromObject = function (obj) {
ModelBaseClass.prototype.fromObject = function(obj) {
for (var key in obj) {
this[key] = obj[key];
}
@ -552,7 +567,7 @@ ModelBaseClass.prototype.fromObject = function (obj) {
* This method does not perform any database operations; it just resets the object to its
* initial state.
*/
ModelBaseClass.prototype.reset = function () {
ModelBaseClass.prototype.reset = function() {
var obj = this;
for (var k in obj) {
if (k !== 'id' && !obj.constructor.dataSource.definitions[obj.constructor.modelName].properties[k]) {
@ -573,20 +588,20 @@ var INSPECT_SUPPORTS_OBJECT_RETVAL =
versionParts[1] > 11 ||
(versionParts[0] === 11 && versionParts[1] >= 14);
ModelBaseClass.prototype.inspect = function (depth) {
ModelBaseClass.prototype.inspect = function(depth) {
if (INSPECT_SUPPORTS_OBJECT_RETVAL)
return this.__data;
return this.__data;
// Workaround for older versions
// See also https://github.com/joyent/node/commit/66280de133
return util.inspect(this.__data, {
showHidden: false,
depth: depth,
colors: false
colors: false,
});
};
ModelBaseClass.mixin = function (anotherClass, options) {
ModelBaseClass.mixin = function(anotherClass, options) {
if (typeof anotherClass === 'string') {
this.modelBuilder.mixins.applyMixin(this, anotherClass, options);
} else {
@ -603,15 +618,15 @@ ModelBaseClass.mixin = function (anotherClass, options) {
}
};
ModelBaseClass.prototype.getDataSource = function () {
ModelBaseClass.prototype.getDataSource = function() {
return this.__dataSource || this.constructor.dataSource;
};
ModelBaseClass.getDataSource = function () {
ModelBaseClass.getDataSource = function() {
return this.dataSource;
};
ModelBaseClass.prototype.setStrict = function (strict) {
ModelBaseClass.prototype.setStrict = function(strict) {
this.__strict = strict;
};

View File

@ -1,3 +1,9 @@
// Copyright IBM Corp. 2015,2016. All Rights Reserved.
// Node module: loopback-datasource-juggler
// This file is licensed under the MIT License.
// License text available at https://opensource.org/licenses/MIT
'use strict';
var async = require('async');
var utils = require('./utils');
@ -103,7 +109,7 @@ ObserverMixin.notifyObserversOf = function(operation, context, callback) {
);
}
},
function(err) { callback(err, context) }
function(err) { callback(err, context); }
);
});
return callback.promise;

File diff suppressed because it is too large Load Diff

View File

@ -1,3 +1,9 @@
// Copyright IBM Corp. 2013,2016. All Rights Reserved.
// Node module: loopback-datasource-juggler
// This file is licensed under the MIT License.
// License text available at https://opensource.org/licenses/MIT
'use strict';
/*!
* Dependencies
*/
@ -16,12 +22,12 @@ function RelationMixin() {
/**
* Define a "one to many" relationship by specifying the model name
*
*
* Examples:
* ```
* User.hasMany(Post, {as: 'posts', foreignKey: 'authorId'});
* ```
*
*
* ```
* Book.hasMany(Chapter);
* ```
@ -34,24 +40,24 @@ function RelationMixin() {
*
* ```js
* Book.create(function(err, book) {
*
*
* // Create a chapter instance ready to be saved in the data source.
* var chapter = book.chapters.build({name: 'Chapter 1'});
*
*
* // Save the new chapter
* chapter.save();
*
*
* // you can also call the Chapter.create method with the `chapters` property which will build a chapter
* // instance and save the it in the data source.
* book.chapters.create({name: 'Chapter 2'}, function(err, savedChapter) {
* // this callback is optional
* });
*
* // Query chapters for the book
* book.chapters(function(err, chapters) { // all chapters with bookId = book.id
*
* // Query chapters for the book
* book.chapters(function(err, chapters) { // all chapters with bookId = book.id
* console.log(chapters);
* });
*
*
* book.chapters({where: {name: 'test'}, function(err, chapters) {
* // All chapters with bookId = book.id and name = 'test'
* console.log(chapters);
@ -86,12 +92,13 @@ RelationMixin.hasMany = function hasMany(modelTo, params) {
* Get the User object for the post author synchronously:
* ```
* post.author();
* ```
* Set the author to be the given user:
* ```
* post.author(user)
* post.author(user)
* ```
* Examples:
*
*
* Suppose the model Post has a *belongsTo* relationship with User (the author of the post). You could declare it this way:
* ```js
* Post.belongsTo(User, {as: 'author', foreignKey: 'userId'});
@ -117,9 +124,9 @@ RelationMixin.hasMany = function hasMany(modelTo, params) {
* @options {Object} params Configuration parameters; see below.
* @property {String} as Name of the property in the referring model that corresponds to the foreign key field in the related model.
* @property {String} foreignKey Name of foreign key property.
*
*
*/
RelationMixin.belongsTo = function (modelTo, params) {
RelationMixin.belongsTo = function(modelTo, params) {
return RelationDefinition.belongsTo(this, modelTo, params);
};
@ -144,9 +151,9 @@ RelationMixin.belongsTo = function (modelTo, params) {
* ```
* Remove the user from the group:
* ```
* user.groups.remove(group, callback);
* user.groups.remove(group, callback);
* ```
*
*
* @param {String|Object} modelTo Model object (or String name of model) to which you are creating the relationship.
* the relation
* @options {Object} params Configuration parameters; see below.

View File

@ -1,9 +1,19 @@
// Copyright IBM Corp. 2013,2016. All Rights Reserved.
// Node module: loopback-datasource-juggler
// This file is licensed under the MIT License.
// License text available at https://opensource.org/licenses/MIT
'use strict';
/*eslint-disable camelcase*/
var i8n = require('inflection');
var utils = require('./utils');
var defineCachedRelations = utils.defineCachedRelations;
var setScopeValuesFromWhere = utils.setScopeValuesFromWhere;
var mergeQuery = utils.mergeQuery;
var DefaultModelBaseClass = require('./model.js');
var collectTargetIds = utils.collectTargetIds;
var idName = utils.idName;
/**
* Module exports
@ -75,24 +85,62 @@ ScopeDefinition.prototype.related = function(receiver, scopeParams, condOrRefres
}
cb = cb || utils.createPromiseCallback();
if (!self.__cachedRelations || self.__cachedRelations[name] === undefined
|| actualRefresh) {
if (!self.__cachedRelations || self.__cachedRelations[name] === undefined ||
actualRefresh) {
// It either doesn't hit the cache or refresh is required
var params = mergeQuery(actualCond, scopeParams, {nestedInclude: true});
var targetModel = this.targetModel(receiver);
targetModel.find(params, options, function (err, data) {
// If there is a through model
// run another query to apply filter on relatedModel(targetModel)
// see github.com/strongloop/loopback-datasource-juggler/issues/166
var scopeOnRelatedModel = params.collect &&
params.include.scope !== null &&
typeof params.include.scope === 'object';
if (scopeOnRelatedModel) {
var filter = params.include;
// The filter applied on relatedModel
var queryRelated = filter.scope;
delete params.include.scope;
};
targetModel.find(params, options, function(err, data) {
if (!err && saveOnCache) {
defineCachedRelations(self);
self.__cachedRelations[name] = data;
}
cb(err, data);
if (scopeOnRelatedModel === true) {
var relatedModel = targetModel.relations[filter.relation].modelTo;
var IdKey = idName(relatedModel);
// Merge queryRelated filter and targetId filter
var buildWhere = function() {
var IdKeyCondition = {};
IdKeyCondition[IdKey] = collectTargetIds(data, IdKey);
var mergedWhere = {
and: [IdKeyCondition, queryRelated.where],
};
return mergedWhere;
};
if (queryRelated.where !== undefined) {
queryRelated.where = buildWhere();
} else {
queryRelated.where = {};
queryRelated.where[IdKey] = collectTargetIds(data, IdKey);
}
relatedModel.find(queryRelated, cb);
} else {
cb(err, data);
}
});
} else {
// Return from cache
cb(null, self.__cachedRelations[name]);
}
return cb.promise;
}
};
/**
* Define a scope method
@ -101,7 +149,7 @@ ScopeDefinition.prototype.related = function(receiver, scopeParams, condOrRefres
*/
ScopeDefinition.prototype.defineMethod = function(name, fn) {
return this.methods[name] = fn;
}
};
/**
* Define a scope to the class
@ -138,10 +186,10 @@ function defineScope(cls, targetClass, name, params, methods, options) {
name: name,
params: params,
methods: methods,
options: options
options: options,
});
if(isStatic) {
if (isStatic) {
cls.scopes = cls.scopes || {};
cls.scopes[name] = definition;
} else {
@ -164,7 +212,7 @@ function defineScope(cls, targetClass, name, params, methods, options) {
* user.accounts.create(act, cb).
*
*/
get: function () {
get: function() {
var targetModel = definition.targetModel(this);
var self = this;
@ -176,8 +224,8 @@ function defineScope(cls, targetClass, name, params, methods, options) {
return self.__cachedRelations[name];
}
} else {
if (typeof condOrRefresh === 'function'
&& options === undefined && cb === undefined) {
if (typeof condOrRefresh === 'function' &&
options === undefined && cb === undefined) {
// customer.orders(cb)
cb = condOrRefresh;
options = {};
@ -187,23 +235,14 @@ function defineScope(cls, targetClass, name, params, methods, options) {
cb = options;
options = {};
}
options = options || {}
options = options || {};
// Check if there is a through model
// see https://github.com/strongloop/loopback/issues/1076
if (f._scope.collect &&
condOrRefresh !== null && typeof condOrRefresh === 'object') {
//extract the paging filters to the through model
['limit','offset','skip','order'].forEach(function(pagerFilter){
if(typeof(condOrRefresh[pagerFilter]) !== 'undefined'){
f._scope[pagerFilter] = condOrRefresh[pagerFilter];
delete condOrRefresh[pagerFilter];
}
});
// Adjust the include so that the condition will be applied to
// the target model
f._scope.include = {
relation: f._scope.collect,
scope: condOrRefresh
scope: condOrRefresh,
};
condOrRefresh = {};
}
@ -221,8 +260,8 @@ function defineScope(cls, targetClass, name, params, methods, options) {
}
f.getAsync = function(condOrRefresh, options, cb) {
if (typeof condOrRefresh === 'function'
&& options === undefined && cb === undefined) {
if (typeof condOrRefresh === 'function' &&
options === undefined && cb === undefined) {
// customer.orders.getAsync(cb)
cb = condOrRefresh;
options = {};
@ -232,9 +271,9 @@ function defineScope(cls, targetClass, name, params, methods, options) {
cb = options;
options = {};
}
options = options || {}
options = options || {};
return definition.related(self, f._scope, condOrRefresh, options, cb);
}
};
f.build = build;
f.create = create;
@ -254,21 +293,21 @@ function defineScope(cls, targetClass, name, params, methods, options) {
// Station.scope('active', {where: {isActive: true}});
// Station.scope('subway', {where: {isUndeground: true}});
// Station.active.subway(cb);
Object.keys(targetClass._scopeMeta).forEach(function (name) {
Object.keys(targetClass._scopeMeta).forEach(function(name) {
Object.defineProperty(f, name, {
enumerable: false,
get: function () {
get: function() {
mergeQuery(f._scope, targetModel._scopeMeta[name]);
return f;
}
},
});
}.bind(self));
return f;
}
},
});
// Wrap the property into a function for remoting
var fn = function () {
var fn = function() {
// primaryObject.scopeName, such as user.accounts
var f = this[name];
// set receiver to be the scope property whose value is a function
@ -277,42 +316,42 @@ function defineScope(cls, targetClass, name, params, methods, options) {
cls['__get__' + name] = fn;
var fn_create = function () {
var fn_create = function() {
var f = this[name].create;
f.apply(this[name], arguments);
};
cls['__create__' + name] = fn_create;
var fn_delete = function () {
var fn_delete = function() {
var f = this[name].destroyAll;
f.apply(this[name], arguments);
};
cls['__delete__' + name] = fn_delete;
var fn_update = function () {
var fn_update = function() {
var f = this[name].updateAll;
f.apply(this[name], arguments);
};
cls['__update__' + name] = fn_update;
var fn_findById = function (cb) {
var fn_findById = function(cb) {
var f = this[name].findById;
f.apply(this[name], arguments);
};
cls['__findById__' + name] = fn_findById;
var fn_findOne = function (cb) {
var fn_findOne = function(cb) {
var f = this[name].findOne;
f.apply(this[name], arguments);
};
cls['__findOne__' + name] = fn_findOne;
var fn_count = function (cb) {
var fn_count = function(cb) {
var f = this[name].count;
f.apply(this[name], arguments);
};
@ -364,7 +403,7 @@ function defineScope(cls, targetClass, name, params, methods, options) {
var targetModel = definition.targetModel(this._receiver);
var scoped = (this._scope && this._scope.where) || {};
var filter = mergeQuery({ where: scoped }, { where: where || {} });
var filter = mergeQuery({where: scoped}, {where: where || {}});
return targetModel.destroyAll(filter.where, options, cb);
}
@ -384,7 +423,7 @@ function defineScope(cls, targetClass, name, params, methods, options) {
options = options || {};
var targetModel = definition.targetModel(this._receiver);
var scoped = (this._scope && this._scope.where) || {};
var filter = mergeQuery({ where: scoped }, { where: where || {} });
var filter = mergeQuery({where: scoped}, {where: where || {}});
return targetModel.updateAll(filter.where, data, options, cb);
}
@ -407,7 +446,7 @@ function defineScope(cls, targetClass, name, params, methods, options) {
}
}
}
options = options || {};
filter = filter || {};
var targetModel = definition.targetModel(this._receiver);
@ -432,7 +471,7 @@ function defineScope(cls, targetClass, name, params, methods, options) {
options = options || {};
var targetModel = definition.targetModel(this._receiver);
var scoped = (this._scope && this._scope.where) || {};
filter = mergeQuery({ where: scoped }, filter || {});
filter = mergeQuery({where: scoped}, filter || {});
return targetModel.findOne(filter, options, cb);
}
@ -450,7 +489,7 @@ function defineScope(cls, targetClass, name, params, methods, options) {
var targetModel = definition.targetModel(this._receiver);
var scoped = (this._scope && this._scope.where) || {};
var filter = mergeQuery({ where: scoped }, { where: where || {} });
var filter = mergeQuery({where: scoped}, {where: where || {}});
return targetModel.count(filter.where, options, cb);
}

View File

@ -1,5 +1,12 @@
// Copyright IBM Corp. 2015,2016. All Rights Reserved.
// Node module: loopback-datasource-juggler
// This file is licensed under the MIT License.
// License text available at https://opensource.org/licenses/MIT
'use strict';
var g = require('strong-globalize')();
var debug = require('debug')('loopback:connector:transaction');
var uuid = require('node-uuid');
var uuid = require('uuid');
var utils = require('./utils');
var jutil = require('./jutil');
var ObserverMixin = require('./observer');
@ -73,7 +80,7 @@ TransactionMixin.beginTransaction = function(options, cb) {
setTimeout(function() {
var context = {
transaction: transaction,
operation: 'timeout'
operation: 'timeout',
};
transaction.notifyObserversOf('timeout', context, function(err) {
if (!err) {
@ -89,7 +96,7 @@ TransactionMixin.beginTransaction = function(options, cb) {
});
} else {
process.nextTick(function() {
var err = new Error('Transaction is not supported');
var err = new Error(g.f('{{Transaction}} is not supported'));
cb(err);
});
}
@ -110,13 +117,13 @@ if (Transaction) {
// Report an error if the transaction is not active
if (!self.connection) {
process.nextTick(function() {
cb(new Error('The transaction is not active: ' + self.id));
cb(new Error(g.f('The {{transaction}} is not active: %s', self.id)));
});
return cb.promise;
}
var context = {
transaction: self,
operation: 'commit'
operation: 'commit',
};
function work(done) {
@ -144,13 +151,13 @@ if (Transaction) {
// Report an error if the transaction is not active
if (!self.connection) {
process.nextTick(function() {
cb(new Error('The transaction is not active: ' + self.id));
cb(new Error(g.f('The {{transaction}} is not active: %s', self.id)));
});
return cb.promise;
}
var context = {
transaction: self,
operation: 'rollback'
operation: 'rollback',
};
function work(done) {
@ -177,5 +184,3 @@ if (Transaction) {
}
TransactionMixin.Transaction = Transaction;

View File

@ -1,3 +1,9 @@
// Copyright IBM Corp. 2013,2016. All Rights Reserved.
// Node module: loopback-datasource-juggler
// This file is licensed under the MIT License.
// License text available at https://opensource.org/licenses/MIT
'use strict';
var Types = {};
/**
* Schema types
@ -9,7 +15,7 @@ Types.Text = function Text(value) {
this.value = value;
}; // Text type
Types.Text.prototype.toObject = Types.Text.prototype.toJSON = function () {
Types.Text.prototype.toObject = Types.Text.prototype.toJSON = function() {
return this.value;
};
@ -19,7 +25,7 @@ Types.JSON = function JSON(value) {
}
this.value = value;
}; // JSON Object
Types.JSON.prototype.toObject = Types.JSON.prototype.toJSON = function () {
Types.JSON.prototype.toObject = Types.JSON.prototype.toJSON = function() {
return this.value;
};
@ -29,20 +35,19 @@ Types.Any = function Any(value) {
}
this.value = value;
}; // Any Type
Types.Any.prototype.toObject = Types.Any.prototype.toJSON = function () {
Types.Any.prototype.toObject = Types.Any.prototype.toJSON = function() {
return this.value;
};
module.exports = function (modelTypes) {
module.exports = function(modelTypes) {
var GeoPoint = require('./geo').GeoPoint;
for(var t in Types) {
for (var t in Types) {
modelTypes[t] = Types[t];
}
modelTypes.schemaTypes = {};
modelTypes.registerType = function (type, names) {
modelTypes.registerType = function(type, names) {
names = names || [];
names = names.concat([type.name]);
for (var n = 0; n < names.length; n++) {
@ -64,4 +69,4 @@ module.exports = function (modelTypes) {
modelTypes.registerType(Object);
};
module.exports.Types = Types;
module.exports.Types = Types;

View File

@ -1,3 +1,9 @@
// Copyright IBM Corp. 2012,2016. All Rights Reserved.
// Node module: loopback-datasource-juggler
// This file is licensed under the MIT License.
// License text available at https://opensource.org/licenses/MIT
'use strict';
exports.safeRequire = safeRequire;
exports.fieldsToArray = fieldsToArray;
exports.selectFields = selectFields;
@ -16,7 +22,10 @@ exports.toRegExp = toRegExp;
exports.hasRegExpFlags = hasRegExpFlags;
exports.idEquals = idEquals;
exports.findIndexOf = findIndexOf;
exports.collectTargetIds = collectTargetIds;
exports.idName = idName;
var g = require('strong-globalize')();
var traverse = require('traverse');
var assert = require('assert');
@ -24,9 +33,9 @@ function safeRequire(module) {
try {
return require(module);
} catch (e) {
console.log('Run "npm install loopback-datasource-juggler ' + module
+ '" command to use loopback-datasource-juggler using ' + module
+ ' database engine');
g.log('Run "{{npm install loopback-datasource-juggler}} %s" command ',
'to use {{loopback-datasource-juggler}} using %s database engine',
module, module);
process.exit(1);
}
}
@ -50,9 +59,8 @@ function setScopeValuesFromWhere(data, where, targetModel) {
var prop = targetModel.definition.properties[i];
if (prop) {
var val = where[i];
if (typeof val !== 'object' || val instanceof prop.type
|| prop.type.name === 'ObjectID') // MongoDB key
{
if (typeof val !== 'object' || val instanceof prop.type ||
prop.type.name === 'ObjectID') { // MongoDB key
// Only pick the {propertyName: propertyValue}
data[i] = where[i];
}
@ -130,9 +138,8 @@ function convertToArray(include) {
if (typeof includeEntry === 'string') {
var obj = {};
obj[includeEntry] = true;
normalized.push(obj)
}
else{
normalized.push(obj);
} else {
normalized.push(includeEntry);
}
}
@ -169,15 +176,14 @@ function mergeQuery(base, update, spec) {
if (!base.include) {
base.include = update.include;
} else {
if (spec.nestedInclude === true){
if (spec.nestedInclude === true) {
//specify nestedInclude=true to force nesting of inclusions on scoped
//queries. e.g. In physician.patients.getAsync({include: 'address'}),
//inclusion should be on patient model, not on physician model.
var saved = base.include;
base.include = {};
base.include[update.include] = saved;
}
else{
} else {
//default behaviour of inclusion merge - merge inclusions at the same
//level. - https://github.com/strongloop/loopback-datasource-juggler/pull/569#issuecomment-95310874
base.include = mergeIncludes(base.include, update.include);
@ -278,7 +284,7 @@ function fieldsToArray(fields, properties, excludeUnknown) {
function selectFields(fields) {
// map function
return function (obj) {
return function(obj) {
var result = {};
var key;
@ -303,14 +309,14 @@ function removeUndefined(query, handleUndefined) {
}
// WARNING: [rfeng] Use map() will cause mongodb to produce invalid BSON
// as traverse doesn't transform the ObjectId correctly
return traverse(query).forEach(function (x) {
return traverse(query).forEach(function(x) {
if (x === undefined) {
switch (handleUndefined) {
case 'nullify':
this.update(null);
break;
case 'throw':
throw new Error('Unexpected `undefined` in query');
throw new Error(g.f('Unexpected `undefined` in query'));
break;
case 'ignore':
default:
@ -318,8 +324,8 @@ function removeUndefined(query, handleUndefined) {
}
}
if (!Array.isArray(x) && (typeof x === 'object' && x !== null
&& x.constructor !== Object)) {
if (!Array.isArray(x) && (typeof x === 'object' && x !== null &&
x.constructor !== Object)) {
// This object is not a plain object
this.update(x, true); // Stop navigating into this object
return x;
@ -425,7 +431,7 @@ function defineCachedRelations(obj) {
writable: true,
enumerable: false,
configurable: true,
value: {}
value: {},
});
}
}
@ -436,15 +442,13 @@ function defineCachedRelations(obj) {
* @returns {boolean}
*/
function isPlainObject(obj) {
return (typeof obj === 'object') && (obj !== null)
&& (obj.constructor === Object);
return (typeof obj === 'object') && (obj !== null) &&
(obj.constructor === Object);
}
function sortObjectsByIds(idName, ids, objects, strict) {
ids = ids.map(function(id) {
return (typeof id === 'object') ? String(id) : id;
return (typeof id === 'object') ? String(id) : id;
});
var indexOf = function(x) {
@ -480,15 +484,15 @@ function createPromiseCallback() {
var cb;
if (!global.Promise) {
cb = function(){};
cb = function() {};
cb.promise = {};
Object.defineProperty(cb.promise, 'then', { get: throwPromiseNotDefined });
Object.defineProperty(cb.promise, 'catch', { get: throwPromiseNotDefined });
Object.defineProperty(cb.promise, 'then', {get: throwPromiseNotDefined});
Object.defineProperty(cb.promise, 'catch', {get: throwPromiseNotDefined});
return cb;
}
var promise = new Promise(function (resolve, reject) {
cb = function (err, data) {
var promise = new Promise(function(resolve, reject) {
cb = function(err, data) {
if (err) return reject(err);
return resolve(data);
};
@ -499,7 +503,7 @@ function createPromiseCallback() {
function throwPromiseNotDefined() {
throw new Error(
'Your Node runtime does support ES6 Promises. ' +
'Your Node runtime does not support ES6 Promises. ' +
'Set "global.Promise" to your preferred implementation of promises.');
}
@ -532,8 +536,8 @@ function toRegExp(regex) {
var isRegExp = regex instanceof RegExp;
if (!(isString || isRegExp))
return new Error('Invalid argument, must be a string, regex literal, or ' +
'RegExp object');
return new Error(g.f('Invalid argument, must be a string, {{regex}} literal, or ' +
'{{RegExp}} object'));
if (isRegExp)
return regex;
@ -552,7 +556,7 @@ function toRegExp(regex) {
var hasInvalidFlags = invalidFlags.length > 0;
if (hasInvalidFlags)
return new Error('Invalid regex flags: ' + invalidFlags);
return new Error(g.f('Invalid {{regex}} flags: %s', invalidFlags));
// strip regex delimiter forward slashes
var expression = regex.substr(1, regex.lastIndexOf('/') - 1);
@ -599,3 +603,30 @@ function findIndexOf(arr, target, isEqual) {
return -1;
}
/**
* Returns an object that queries targetIds.
* @param {Array} The array of targetData
* @param {String} The Id property name of target model
* @returns {Object} The object that queries targetIds
*/
function collectTargetIds(targetData, idPropertyName) {
var targetIds = [];
for (var i = 0; i < targetData.length; i++) {
var targetId = targetData[i][idPropertyName];
targetIds.push(targetId);
};
var IdQuery = {
inq: uniq(targetIds),
};
return IdQuery;
}
/**
* Find the idKey of a Model.
* @param {ModelConstructor} m - Model Constructor
* @returns {String}
*/
function idName(m) {
return m.definition.idName() || 'id';
}

View File

@ -1,3 +1,10 @@
// Copyright IBM Corp. 2013,2016. All Rights Reserved.
// Node module: loopback-datasource-juggler
// This file is licensed under the MIT License.
// License text available at https://opensource.org/licenses/MIT
'use strict';
var g = require('strong-globalize')();
var util = require('util');
var extend = util._extend;
@ -121,9 +128,11 @@ Validatable.validatesNumericalityOf = getConfigurator('numericality');
* ```
*
* @param {String} propertyName Property name to validate.
* @options {Object} Options
* @options {Object} Options See below.
* @property {Array} inArray Property must match one of the values in the array to be valid.
* @property {String} message Optional error message if property is not valid. Default error message: "is not included in the list".
* @property {String} message Optional error message if property is not valid.
* Default error message: "is not included in the list".
* @property {Boolean} allowNull Whether null values are allowed.
*/
Validatable.validatesInclusionOf = getConfigurator('inclusion');
@ -136,6 +145,7 @@ Validatable.validatesInclusionOf = getConfigurator('inclusion');
* @options {Object} Options
* @property {Array} inArray Property must match one of the values in the array to be valid.
* @property {String} message Optional error message if property is not valid. Default error message: "is reserved".
* @property {Boolean} allowNull Whether null values are allowed.
*/
Validatable.validatesExclusionOf = getConfigurator('exclusion');
@ -143,12 +153,13 @@ Validatable.validatesExclusionOf = getConfigurator('exclusion');
* Validate format. Require a model to include a property that matches the given format.
*
* Require a model to include a property that matches the given format. Example:
* `User.validatesFormat('name', {with: /\w+/});`
* `User.validatesFormatOf('name', {with: /\w+/});`
*
* @param {String} propertyName Property name to validate.
* @options {Object} Options
* @property {RegExp} with Regular expression to validate format.
* @property {String} message Optional error message if property is not valid. Default error message: " is invalid".
* @property {Boolean} allowNull Whether null values are allowed.
*/
Validatable.validatesFormatOf = getConfigurator('format');
@ -170,6 +181,7 @@ Validatable.validatesFormatOf = getConfigurator('format');
* @param {Function} validatorFn Custom validation function.
* @options {Object} Options See below.
* @property {String} message Optional error message if property is not valid. Default error message: " is invalid".
* @property {Boolean} allowNull Whether null values are allowed.
*/
Validatable.validate = getConfigurator('custom');
@ -199,8 +211,9 @@ Validatable.validate = getConfigurator('custom');
*```
* @param {String} propertyName Property name to validate.
* @param {Function} validatorFn Custom validation function.
* @options {Object} Options See below
* @options {Object} Options See below.
* @property {String} message Optional error message if property is not valid. Default error message: " is invalid".
* @property {Boolean} allowNull Whether null values are allowed.
*/
Validatable.validateAsync = getConfigurator('custom', {async: true});
@ -225,6 +238,7 @@ Validatable.validateAsync = getConfigurator('custom', {async: true});
* @property {RegExp} with Regular expression to validate format.
* @property {Array.<String>} scopedTo List of properties defining the scope.
* @property {String} message Optional error message if property is not valid. Default error message: "is not unique".
* @property {Boolean} allowNull Whether null values are allowed.
*/
Validatable.validatesUniquenessOf = getConfigurator('uniqueness', {async: true});
@ -233,7 +247,7 @@ Validatable.validatesUniquenessOf = getConfigurator('uniqueness', {async: true})
/*!
* Presence validator
*/
function validatePresence(attr, conf, err) {
function validatePresence(attr, conf, err, options) {
if (blank(this[attr])) {
err();
}
@ -242,7 +256,7 @@ function validatePresence(attr, conf, err) {
/*!
* Absence validator
*/
function validateAbsence(attr, conf, err) {
function validateAbsence(attr, conf, err, options) {
if (!blank(this[attr])) {
err();
}
@ -251,7 +265,7 @@ function validateAbsence(attr, conf, err) {
/*!
* Length validator
*/
function validateLength(attr, conf, err) {
function validateLength(attr, conf, err, options) {
if (nullCheck.call(this, attr, conf, err)) return;
var len = this[attr].length;
@ -269,7 +283,7 @@ function validateLength(attr, conf, err) {
/*!
* Numericality validator
*/
function validateNumericality(attr, conf, err) {
function validateNumericality(attr, conf, err, options) {
if (nullCheck.call(this, attr, conf, err)) return;
if (typeof this[attr] !== 'number') {
@ -283,29 +297,29 @@ function validateNumericality(attr, conf, err) {
/*!
* Inclusion validator
*/
function validateInclusion(attr, conf, err) {
function validateInclusion(attr, conf, err, options) {
if (nullCheck.call(this, attr, conf, err)) return;
if (!~conf.in.indexOf(this[attr])) {
err()
err();
}
}
/*!
* Exclusion validator
*/
function validateExclusion(attr, conf, err) {
function validateExclusion(attr, conf, err, options) {
if (nullCheck.call(this, attr, conf, err)) return;
if (~conf.in.indexOf(this[attr])) {
err()
err();
}
}
/*!
* Format validator
*/
function validateFormat(attr, conf, err) {
function validateFormat(attr, conf, err, options) {
if (nullCheck.call(this, attr, conf, err)) return;
if (typeof this[attr] === 'string') {
@ -320,14 +334,22 @@ function validateFormat(attr, conf, err) {
/*!
* Custom validator
*/
function validateCustom(attr, conf, err, done) {
function validateCustom(attr, conf, err, options, done) {
if (typeof options === 'function') {
done = options;
options = {};
}
conf.customValidator.call(this, err, done);
}
/*!
* Uniqueness validator
*/
function validateUniqueness(attr, conf, err, done) {
function validateUniqueness(attr, conf, err, options, done) {
if (typeof options === 'function') {
done = options;
options = {};
}
if (blank(this[attr])) {
return process.nextTick(done);
}
@ -344,14 +366,15 @@ function validateUniqueness(attr, conf, err, done) {
var idName = this.constructor.definition.idName();
var isNewRecord = this.isNewRecord();
this.constructor.find(cond, function (error, found) {
this.constructor.find(cond, options, function(error, found) {
if (error) {
err(error);
} else if (found.length > 1) {
err();
} else if (found.length === 1 && idName === attr && isNewRecord) {
err();
} else if (found.length === 1 && (!this.id || !found[0].id || found[0].id.toString() != this.id.toString())) {
} else if (found.length === 1 && (!this.id || !found[0].id ||
found[0].id.toString() != this.id.toString())) {
err();
}
done();
@ -367,11 +390,11 @@ var validators = {
exclusion: validateExclusion,
format: validateFormat,
custom: validateCustom,
uniqueness: validateUniqueness
uniqueness: validateUniqueness,
};
function getConfigurator(name, opts) {
return function () {
return function() {
var args = Array.prototype.slice.call(arguments);
args[1] = args[1] || {};
configure(this, name, args, opts);
@ -410,7 +433,8 @@ function getConfigurator(name, opts) {
* @param {Function} callback called with (valid)
* @returns {Boolean} True if no asynchronous validation is configured and all properties pass validation.
*/
Validatable.prototype.isValid = function (callback, data) {
Validatable.prototype.isValid = function(callback, data, options) {
options = options || {};
var valid = true, inst = this, wait = 0, async = false;
var validations = this.constructor.validations;
@ -421,8 +445,8 @@ Validatable.prototype.isValid = function (callback, data) {
if (typeof validations !== 'object' && !reportDiscardedProperties) {
cleanErrors(this);
if (callback) {
this.trigger('validate', function (validationsDone) {
validationsDone.call(inst, function () {
this.trigger('validate', function(validationsDone) {
validationsDone.call(inst, function() {
callback(valid);
});
}, data, callback);
@ -433,10 +457,10 @@ Validatable.prototype.isValid = function (callback, data) {
Object.defineProperty(this, 'errors', {
enumerable: false,
configurable: true,
value: new Errors
value: new Errors,
});
this.trigger('validate', function (validationsDone) {
this.trigger('validate', function(validationsDone) {
var inst = this,
asyncFail = false;
@ -448,8 +472,8 @@ Validatable.prototype.isValid = function (callback, data) {
if (v.options && v.options.async) {
async = true;
wait += 1;
process.nextTick(function () {
validationFailed(inst, attr, v, done);
process.nextTick(function() {
validationFailed(inst, attr, v, options, done);
});
} else {
if (validationFailed(inst, attr, v)) {
@ -470,7 +494,7 @@ Validatable.prototype.isValid = function (callback, data) {
}
if (!async) {
validationsDone.call(inst, function () {
validationsDone.call(inst, function() {
if (valid) cleanErrors(inst);
if (callback) {
callback(valid);
@ -481,7 +505,7 @@ Validatable.prototype.isValid = function (callback, data) {
function done(fail) {
asyncFail = asyncFail || fail;
if (--wait === 0) {
validationsDone.call(inst, function () {
validationsDone.call(inst, function() {
if (valid && !asyncFail) cleanErrors(inst);
if (callback) {
callback(valid && !asyncFail);
@ -489,7 +513,6 @@ Validatable.prototype.isValid = function (callback, data) {
});
}
}
}, data, callback);
if (async) {
@ -499,26 +522,30 @@ Validatable.prototype.isValid = function (callback, data) {
} else {
return valid;
}
};
function cleanErrors(inst) {
Object.defineProperty(inst, 'errors', {
enumerable: false,
configurable: true,
value: false
value: false,
});
}
function validationFailed(inst, attr, conf, cb) {
function validationFailed(inst, attr, conf, options, cb) {
var opts = conf.options || {};
if (typeof options === 'function') {
cb = options;
options = {};
}
if (typeof attr !== 'string') return false;
// here we should check skip validation conditions (if, unless)
// that can be specified in conf
if (skipValidation(inst, conf, 'if')
|| skipValidation(inst, conf, 'unless')) {
if (skipValidation(inst, conf, 'if') ||
skipValidation(inst, conf, 'unless')) {
if (cb) cb(false);
return false;
}
@ -553,8 +580,9 @@ function validationFailed(inst, attr, conf, cb) {
if (kind !== false) inst.errors.add(attr, message, code);
fail = true;
});
validatorArguments.push(options);
if (cb) {
validatorArguments.push(function () {
validatorArguments.push(function() {
cb(fail);
});
}
@ -588,19 +616,19 @@ var defaultMessages = {
length: {
min: 'too short',
max: 'too long',
is: 'length is wrong'
is: 'length is wrong',
},
common: {
blank: 'is blank',
'null': 'is null'
'null': 'is null',
},
numericality: {
'int': 'is not an integer',
'number': 'is not a number'
'number': 'is not a number',
},
inclusion: 'is not included in the list',
exclusion: 'is reserved',
uniqueness: 'is not unique'
uniqueness: 'is not unique',
};
function nullCheck(attr, conf, err) {
@ -642,7 +670,7 @@ function configure(cls, validation, args, opts) {
writable: true,
configurable: true,
enumerable: false,
value: {}
value: {},
});
}
args = [].slice.call(args);
@ -656,7 +684,7 @@ function configure(cls, validation, args, opts) {
conf.customValidator = args.pop();
}
conf.validation = validation;
args.forEach(function (attr) {
args.forEach(function(attr) {
if (typeof attr === 'string') {
var validation = extend({}, conf);
validation.options = opts || {};
@ -670,11 +698,11 @@ function Errors() {
Object.defineProperty(this, 'codes', {
enumerable: false,
configurable: true,
value: {}
value: {},
});
}
Errors.prototype.add = function (field, message, code) {
Errors.prototype.add = function(field, message, code) {
code = code || 'invalid';
if (!this[field]) {
this[field] = [];
@ -686,7 +714,7 @@ Errors.prototype.add = function (field, message, code) {
function ErrorCodes(messages) {
var c = this;
Object.keys(messages).forEach(function (field) {
Object.keys(messages).forEach(function(field) {
c[field] = messages[field].codes;
});
}
@ -747,7 +775,7 @@ function ValidationError(obj) {
this.name = 'ValidationError';
var context = obj && obj.constructor && obj.constructor.modelName;
this.message = util.format(
this.message = g.f(
'The %s instance is not valid. Details: %s.',
context ? '`' + context + '`' : 'model',
formatErrors(obj.errors, obj.toJSON()) || '(unknown)'
@ -758,7 +786,7 @@ function ValidationError(obj) {
this.details = {
context: context,
codes: obj.errors && obj.errors.codes,
messages: obj.errors
messages: obj.errors,
};
if (Error.captureStackTrace) {
@ -808,7 +836,7 @@ function formatPropertyError(propertyName, propertyValue, errorMessage) {
showHidden: false,
color: false,
// show top-level object properties only
depth: Array.isArray(propertyValue) ? 1 : 0
depth: Array.isArray(propertyValue) ? 1 : 0,
});
formattedValue = truncatePropertyString(formattedValue);
} else {
@ -834,5 +862,5 @@ function truncatePropertyString(value) {
len -= 3;
}
return value.slice(0, len-4) + '...' + tail;
return value.slice(0, len - 4) + '...' + tail;
}

View File

@ -1,6 +1,10 @@
{
"name": "loopback-datasource-juggler",
"version": "2.44.0",
"version": "2.57.0",
"publishConfig": {
"tag": "lts",
"export-tests": true
},
"description": "LoopBack DataSoure Juggler",
"keywords": [
"StrongLoop",
@ -20,18 +24,21 @@
"depd": "./lib/browser.depd.js"
},
"scripts": {
"clean": "make clean",
"help": "make help",
"refresh": "make refresh",
"test": "make test"
"lint": "eslint .",
"test": "mocha",
"posttest": "npm run lint"
},
"engines": {
"node": ">=6"
},
"engines": [
"node >= 0.6"
],
"devDependencies": {
"async-iterators": "^0.2.2",
"bluebird": "^2.9.9",
"eslint": "^2.13.1",
"eslint-config-loopback": "^4.0.0",
"loopback-connector-throwing": "file:./test/fixtures/loopback-connector-throwing",
"mocha": "^2.1.0",
"should": "^5.0.0"
"should": "^8.0.2"
},
"dependencies": {
"async": "~1.0.0",
@ -39,9 +46,12 @@
"depd": "^1.0.0",
"inflection": "^1.6.0",
"loopback-connector": "^2.1.0",
"node-uuid": "^1.4.2",
"qs": "^3.1.0",
"traverse": "^0.6.6"
"minimatch": "^3.0.3",
"qs": "^6.5.0",
"shortid": "^2.2.6",
"strong-globalize": "^2.6.2",
"traverse": "^0.6.6",
"uuid": "^3.0.1"
},
"license": "MIT"
}

View File

@ -1,3 +1,10 @@
// Copyright IBM Corp. 2015,2016. All Rights Reserved.
// Node module: loopback-datasource-juggler
// This file is licensed under the MIT License.
// License text available at https://opensource.org/licenses/MIT
/* eslint-disable camelcase */
/*
* Describe context objects of operation hooks in comprehensive HTML table.
* Usage:
@ -12,12 +19,12 @@ var Memory = require('../lib/connectors/memory').Memory;
var HOOK_NAMES = [
'access',
'before save', 'persist', 'after save',
'before delete', 'after delete'
'before delete', 'after delete',
];
var dataSources = [
createOptimizedDataSource(),
createUnoptimizedDataSource()
createUnoptimizedDataSource(),
];
var observedContexts = [];
@ -29,7 +36,7 @@ Promise.onPossiblyUnhandledRejection(function(err) {
var operations = [
function find(ds) {
return ds.TestModel.find({ where: { id: '1' } });
return ds.TestModel.find({ where: { id: '1' }});
},
function count(ds) {
@ -42,13 +49,13 @@ var operations = [
function findOrCreate_found(ds) {
return ds.TestModel.findOrCreate(
{ where: { name: ds.existingInstance.name } },
{ where: { name: ds.existingInstance.name }},
{ name: ds.existingInstance.name });
},
function findOrCreate_create(ds) {
return ds.TestModel.findOrCreate(
{ where: { name: 'new-record' } },
{ where: { name: 'new-record' }},
{ name: 'new-record' });
},
@ -61,6 +68,21 @@ var operations = [
{ id: ds.existingInstance.id, name: 'new name' });
},
function replaceOrCreate_create(ds) {
return ds.TestModel.replaceOrCreate({ id: 'not-found', name: 'not found' });
},
function replaceOrCreate_update(ds) {
return ds.TestModel.replaceOrCreate(
{ id: ds.existingInstance.id, name: 'new name' });
},
function replaceById(ds) {
return ds.TestModel.replaceById(
ds.existingInstance.id,
{ name: 'new name' });
},
function updateAll(ds) {
return ds.TestModel.updateAll({ name: 'searched' }, { name: 'updated' });
},
@ -88,22 +110,11 @@ operations.forEach(function(op) {
p = p.then(runner(op));
});
p.then(report, console.error);
p.then(report, function(err) { console.error(err.stack); });
function createOptimizedDataSource() {
var ds = new DataSource({ connector: Memory });
ds.name = 'Optimized';
ds.connector.findOrCreate = function (model, query, data, callback) {
this.all(model, query, {}, function (err, list) {
if (err || (list && list[0])) return callback(err, list && list[0], false);
this.create(model, data, {}, function (err) {
callback(err, data, true);
});
}.bind(this));
};
return ds;
}
@ -114,6 +125,7 @@ function createUnoptimizedDataSource() {
// disable optimized methods
ds.connector.updateOrCreate = false;
ds.connector.findOrCreate = false;
ds.connector.replaceOrCreate = false;
return ds;
}
@ -123,7 +135,7 @@ function setupTestModels() {
var TestModel = ds.TestModel = ds.createModel('TestModel', {
id: { type: String, id: true, default: uid },
name: { type: String, required: true },
extra: { type: String, required: false }
extra: { type: String, required: false },
});
});
return Promise.resolve();
@ -144,7 +156,7 @@ function runner(fn) {
observedContexts.push({
operation: fn.name,
connector: ds.name,
hooks: {}
hooks: {},
});
return fn(ds);
});
@ -160,11 +172,11 @@ function resetStorage(ds) {
});
return TestModel.deleteAll()
.then(function() {
return TestModel.create({ name: 'first' })
return TestModel.create({ name: 'first' });
})
.then(function(instance) {
// Look it up from DB so that default values are retrieved
return TestModel.findById(instance.id)
return TestModel.findById(instance.id);
})
.then(function(instance) {
ds.existingInstance = instance;
@ -173,7 +185,7 @@ function resetStorage(ds) {
.then(function() {
HOOK_NAMES.forEach(function(hook) {
TestModel.observe(hook, function(ctx, next) {
var row = observedContexts[observedContexts.length-1];
var row = observedContexts[observedContexts.length - 1];
row.hooks[hook] = Object.keys(ctx);
next();
});
@ -182,7 +194,7 @@ function resetStorage(ds) {
}
function report() {
console.log('<style>')
console.log('<style>');
console.log('td { font-family: "monospace": }');
console.log('td, th {');
console.log(' vertical-align: text-top;');
@ -193,7 +205,7 @@ function report() {
// merge rows where Optimized and Unoptimized produce the same context
observedContexts.forEach(function(row, ix) {
if (!ix) return;
var last = observedContexts[ix-1];
var last = observedContexts[ix - 1];
if (row.operation != last.operation) return;
if (JSON.stringify(row.hooks) !== JSON.stringify(last.hooks)) return;
last.merge = true;

View File

@ -1,39 +1,45 @@
// Copyright IBM Corp. 2015,2016. All Rights Reserved.
// Node module: loopback-datasource-juggler
// This file is licensed under the MIT License.
// License text available at https://opensource.org/licenses/MIT
'use strict';
var should = require('./init.js');
var jdb = require('../');
var DataSource = jdb.DataSource;
var ds, Item, Variant;
describe('Datasource-specific field types for foreign keys', function () {
before(function () {
describe('Datasource-specific field types for foreign keys', function() {
before(function() {
ds = new DataSource('memory');
Item = ds.define('Item', {
"myProp": {
"id": true,
"type": "string",
"memory": {
"dataType": "string"
}
}
'myProp': {
'id': true,
'type': 'string',
'memory': {
'dataType': 'string',
},
},
});
Variant = ds.define('Variant', {}, {
relations: {
"item": {
"type": "belongsTo",
"as": "item",
"model": "Item",
"foreignKey": "myProp"
}
}
'item': {
'type': 'belongsTo',
'as': 'item',
'model': 'Item',
'foreignKey': 'myProp',
},
},
});
});
it('should create foreign key with database-specific field type', function (done) {
it('should create foreign key with database-specific field type', function(done) {
var VariantDefinition = ds.getModelDefinition('Variant');
should.exist(VariantDefinition);
should.exist(VariantDefinition.properties.myProp.memory);
should.exist(VariantDefinition.properties.myProp.memory.dataType);
VariantDefinition.properties.myProp.memory.dataType.should.be.equal("string");
VariantDefinition.properties.myProp.memory.dataType.should.be.equal('string');
done();
});
})

View File

@ -1,3 +1,9 @@
// Copyright IBM Corp. 2015,2016. All Rights Reserved.
// Node module: loopback-datasource-juggler
// This file is licensed under the MIT License.
// License text available at https://opensource.org/licenses/MIT
'use strict';
var ModelBuilder = require('../').ModelBuilder;
var should = require('./init');
@ -5,7 +11,7 @@ describe('async observer', function() {
var TestModel;
beforeEach(function defineTestModel() {
var modelBuilder = new ModelBuilder();
TestModel = modelBuilder.define('TestModel', { name: String });
TestModel = modelBuilder.define('TestModel', {name: String});
});
it('calls registered async observers', function(done) {
@ -155,7 +161,7 @@ describe('async observer', function() {
it('passes context to final callback', function(done) {
var context = {};
TestModel.notifyObserversOf('event', context, function(err, ctx) {
(ctx || "null").should.equal(context);
(ctx || 'null').should.equal(context);
done();
});
});
@ -297,7 +303,7 @@ describe('async observer', function() {
});
it('returns a promise when no callback is provided', function() {
var context = { value: 'a-test-context' };
var context = {value: 'a-test-context'};
var p = TestModel.notifyObserversOf('event', context);
(p !== undefined).should.be.true;
return p.then(function(result) {

View File

@ -1,11 +1,16 @@
// Copyright IBM Corp. 2013,2016. All Rights Reserved.
// Node module: loopback-datasource-juggler
// This file is licensed under the MIT License.
// License text available at https://opensource.org/licenses/MIT
'use strict';
// This test written in mocha+should.js
var should = require('./init.js');
var async = require('async');
var db, User;
describe('basic-querying', function () {
before(function (done) {
describe('basic-querying', function() {
before(function(done) {
db = getSchema();
User = db.define('User', {
seq: {type: Number, index: true},
@ -14,41 +19,39 @@ describe('basic-querying', function () {
birthday: {type: Date, index: true},
role: {type: String, index: true},
order: {type: Number, index: true, sort: true},
vip: {type: Boolean}
vip: {type: Boolean},
});
db.automigrate(done);
});
describe('ping', function () {
it('should be able to test connections', function (done) {
db.ping(function (err) {
describe('ping', function() {
it('should be able to test connections', function(done) {
db.ping(function(err) {
should.not.exist(err);
done();
});
});
});
describe('findById', function () {
before(function (done) {
describe('findById', function() {
before(function(done) {
User.destroyAll(done);
});
it('should query by id: not found', function (done) {
User.findById(1, function (err, u) {
it('should query by id: not found', function(done) {
User.findById(1, function(err, u) {
should.not.exist(u);
should.not.exist(err);
done();
});
});
it('should query by id: found', function (done) {
User.create(function (err, u) {
it('should query by id: found', function(done) {
User.create(function(err, u) {
should.not.exist(err);
should.exist(u.id);
User.findById(u.id, function (err, u) {
User.findById(u.id, function(err, u) {
should.exist(u);
should.not.exist(err);
u.should.be.an.instanceOf(User);
@ -56,19 +59,18 @@ describe('basic-querying', function () {
});
});
});
});
describe('findByIds', function () {
describe('findByIds', function() {
var createdUsers;
before(function(done) {
var people = [
{ name: 'a', vip: true },
{ name: 'b' },
{ name: 'c' },
{ name: 'd', vip: true },
{ name: 'e' },
{ name: 'f' }
{name: 'a', vip: true},
{name: 'b'},
{name: 'c'},
{name: 'd', vip: true},
{name: 'e'},
{name: 'f'},
];
db.automigrate(['User'], function(err) {
User.create(people, function(err, users) {
@ -98,11 +100,11 @@ describe('basic-querying', function () {
it('should query by ids and condition', function(done) {
User.findByIds([
createdUsers[0].id,
createdUsers[1].id,
createdUsers[2].id,
createdUsers[3].id],
{ where: { vip: true } }, function(err, users) {
createdUsers[0].id,
createdUsers[1].id,
createdUsers[2].id,
createdUsers[3].id],
{where: {vip: true}}, function(err, users) {
should.exist(users);
should.not.exist(err);
var names = users.map(function(u) {
@ -117,15 +119,21 @@ describe('basic-querying', function () {
done();
});
});
});
describe('find', function () {
describe('find', function() {
before(seed);
it('should query collection', function (done) {
User.find(function (err, users) {
before(function setupDelayingLoadedHook() {
User.observe('loaded', nextAfterDelay);
});
after(function removeDelayingLoadHook() {
User.removeObserver('loaded', nextAfterDelay);
});
it('should query collection', function(done) {
User.find(function(err, users) {
should.exists(users);
should.not.exists(err);
users.should.have.lengthOf(6);
@ -133,8 +141,8 @@ describe('basic-querying', function () {
});
});
it('should query limited collection', function (done) {
User.find({limit: 3}, function (err, users) {
it('should query limited collection', function(done) {
User.find({limit: 3}, function(err, users) {
should.exists(users);
should.not.exists(err);
users.should.have.lengthOf(3);
@ -142,8 +150,8 @@ describe('basic-querying', function () {
});
});
it('should query collection with skip & limit', function (done) {
User.find({skip: 1, limit: 4, order: 'seq'}, function (err, users) {
it('should query collection with skip & limit', function(done) {
User.find({skip: 1, limit: 4, order: 'seq'}, function(err, users) {
should.exists(users);
should.not.exists(err);
users[0].seq.should.be.eql(1);
@ -152,8 +160,8 @@ describe('basic-querying', function () {
});
});
it('should query collection with offset & limit', function (done) {
User.find({offset: 2, limit: 3, order: 'seq'}, function (err, users) {
it('should query collection with offset & limit', function(done) {
User.find({offset: 2, limit: 3, order: 'seq'}, function(err, users) {
should.exists(users);
should.not.exists(err);
users[0].seq.should.be.eql(2);
@ -162,8 +170,8 @@ describe('basic-querying', function () {
});
});
it('should query filtered collection', function (done) {
User.find({where: {role: 'lead'}}, function (err, users) {
it('should query filtered collection', function(done) {
User.find({where: {role: 'lead'}}, function(err, users) {
should.exists(users);
should.not.exists(err);
users.should.have.lengthOf(2);
@ -171,30 +179,30 @@ describe('basic-querying', function () {
});
});
it('should query collection sorted by numeric field', function (done) {
User.find({order: 'order'}, function (err, users) {
it('should query collection sorted by numeric field', function(done) {
User.find({order: 'order'}, function(err, users) {
should.exists(users);
should.not.exists(err);
users.forEach(function (u, i) {
users.forEach(function(u, i) {
u.order.should.eql(i + 1);
});
done();
});
});
it('should query collection desc sorted by numeric field', function (done) {
User.find({order: 'order DESC'}, function (err, users) {
it('should query collection desc sorted by numeric field', function(done) {
User.find({order: 'order DESC'}, function(err, users) {
should.exists(users);
should.not.exists(err);
users.forEach(function (u, i) {
users.forEach(function(u, i) {
u.order.should.eql(users.length - i);
});
done();
});
});
it('should query collection sorted by string field', function (done) {
User.find({order: 'name'}, function (err, users) {
it('should query collection sorted by string field', function(done) {
User.find({order: 'name'}, function(err, users) {
should.exists(users);
should.not.exists(err);
users.shift().name.should.equal('George Harrison');
@ -204,8 +212,8 @@ describe('basic-querying', function () {
});
});
it('should query collection desc sorted by string field', function (done) {
User.find({order: 'name DESC'}, function (err, users) {
it('should query collection desc sorted by string field', function(done) {
User.find({order: 'name DESC'}, function(err, users) {
should.exists(users);
should.not.exists(err);
users.pop().name.should.equal('George Harrison');
@ -215,53 +223,65 @@ describe('basic-querying', function () {
});
});
it('should support "and" operator that is satisfied', function (done) {
it('should query sorted desc by order integer field even though there' +
'is an async model loaded hook', function(done) {
User.find({order: 'order DESC'}, function(err, users) {
if (err) return done(err);
should.exists(users);
var order = users.map(function(u) { return u.order; });
order.should.eql([6, 5, 4, 3, 2, 1]);
done();
});
});
it('should support "and" operator that is satisfied', function(done) {
User.find({where: {and: [
{name: 'John Lennon'},
{role: 'lead'}
]}}, function (err, users) {
{role: 'lead'},
]}}, function(err, users) {
should.not.exist(err);
users.should.have.property('length', 1);
done();
});
});
it('should support "and" operator that is not satisfied', function (done) {
it('should support "and" operator that is not satisfied', function(done) {
User.find({where: {and: [
{name: 'John Lennon'},
{role: 'member'}
]}}, function (err, users) {
{role: 'member'},
]}}, function(err, users) {
should.not.exist(err);
users.should.have.property('length', 0);
done();
});
});
it('should support "or" that is satisfied', function (done) {
it('should support "or" that is satisfied', function(done) {
User.find({where: {or: [
{name: 'John Lennon'},
{role: 'lead'}
]}}, function (err, users) {
{role: 'lead'},
]}}, function(err, users) {
should.not.exist(err);
users.should.have.property('length', 2);
done();
});
});
it('should support "or" operator that is not satisfied', function (done) {
it('should support "or" operator that is not satisfied', function(done) {
User.find({where: {or: [
{name: 'XYZ'},
{role: 'Hello1'}
]}}, function (err, users) {
{role: 'Hello1'},
]}}, function(err, users) {
should.not.exist(err);
users.should.have.property('length', 0);
done();
});
});
it('should support date "gte" that is satisfied', function (done) {
User.find({order: 'seq', where: { birthday: { "gte": new Date('1980-12-08') }
}}, function (err, users) {
it('should support date "gte" that is satisfied', function(done) {
User.find({order: 'seq', where: {birthday: {'gte': new Date('1980-12-08')},
}}, function(err, users) {
should.not.exist(err);
users.should.have.property('length', 1);
users[0].name.should.equal('John Lennon');
@ -269,18 +289,18 @@ describe('basic-querying', function () {
});
});
it('should support date "gt" that is not satisfied', function (done) {
User.find({order: 'seq', where: { birthday: { "gt": new Date('1980-12-08') }
}}, function (err, users) {
it('should support date "gt" that is not satisfied', function(done) {
User.find({order: 'seq', where: {birthday: {'gt': new Date('1980-12-08')},
}}, function(err, users) {
should.not.exist(err);
users.should.have.property('length', 0);
done();
});
});
it('should support date "gt" that is satisfied', function (done) {
User.find({order: 'seq', where: { birthday: { "gt": new Date('1980-12-07') }
}}, function (err, users) {
it('should support date "gt" that is satisfied', function(done) {
User.find({order: 'seq', where: {birthday: {'gt': new Date('1980-12-07')},
}}, function(err, users) {
should.not.exist(err);
users.should.have.property('length', 1);
users[0].name.should.equal('John Lennon');
@ -288,9 +308,9 @@ describe('basic-querying', function () {
});
});
it('should support date "lt" that is satisfied', function (done) {
User.find({order: 'seq', where: { birthday: { "lt": new Date('1980-12-07') }
}}, function (err, users) {
it('should support date "lt" that is satisfied', function(done) {
User.find({order: 'seq', where: {birthday: {'lt': new Date('1980-12-07')},
}}, function(err, users) {
should.not.exist(err);
users.should.have.property('length', 1);
users[0].name.should.equal('Paul McCartney');
@ -298,9 +318,9 @@ describe('basic-querying', function () {
});
});
it('should support number "gte" that is satisfied', function (done) {
User.find({order: 'seq', where: { order: { "gte": 3}
}}, function (err, users) {
it('should support number "gte" that is satisfied', function(done) {
User.find({order: 'seq', where: {order: {'gte': 3},
}}, function(err, users) {
should.not.exist(err);
users.should.have.property('length', 4);
users[0].name.should.equal('George Harrison');
@ -308,18 +328,18 @@ describe('basic-querying', function () {
});
});
it('should support number "gt" that is not satisfied', function (done) {
User.find({order: 'seq', where: { order: { "gt": 6 }
}}, function (err, users) {
it('should support number "gt" that is not satisfied', function(done) {
User.find({order: 'seq', where: {order: {'gt': 6},
}}, function(err, users) {
should.not.exist(err);
users.should.have.property('length', 0);
done();
});
});
it('should support number "gt" that is satisfied', function (done) {
User.find({order: 'seq', where: { order: { "gt": 5 }
}}, function (err, users) {
it('should support number "gt" that is satisfied', function(done) {
User.find({order: 'seq', where: {order: {'gt': 5},
}}, function(err, users) {
should.not.exist(err);
users.should.have.property('length', 1);
users[0].name.should.equal('Ringo Starr');
@ -327,9 +347,9 @@ describe('basic-querying', function () {
});
});
it('should support number "lt" that is satisfied', function (done) {
User.find({order: 'seq', where: { order: { "lt": 2 }
}}, function (err, users) {
it('should support number "lt" that is satisfied', function(done) {
User.find({order: 'seq', where: {order: {'lt': 2},
}}, function(err, users) {
should.not.exist(err);
users.should.have.property('length', 1);
users[0].name.should.equal('Paul McCartney');
@ -337,36 +357,36 @@ describe('basic-querying', function () {
});
});
it('should support number "gt" that is satisfied by null value', function (done) {
User.find({order: 'seq', where: { order: { "gt": null }
}}, function (err, users) {
it('should support number "gt" that is satisfied by null value', function(done) {
User.find({order: 'seq', where: {order: {'gt': null},
}}, function(err, users) {
should.not.exist(err);
users.should.have.property('length', 0);
done();
});
});
it('should support number "lt" that is not satisfied by null value', function (done) {
User.find({order: 'seq', where: { order: { "lt": null }
}}, function (err, users) {
it('should support number "lt" that is not satisfied by null value', function(done) {
User.find({order: 'seq', where: {order: {'lt': null},
}}, function(err, users) {
should.not.exist(err);
users.should.have.property('length', 0);
done();
});
});
it('should support string "gte" that is satisfied by null value', function (done) {
User.find({order: 'seq', where: { name: { "gte": null}
}}, function (err, users) {
it('should support string "gte" that is satisfied by null value', function(done) {
User.find({order: 'seq', where: {name: {'gte': null},
}}, function(err, users) {
should.not.exist(err);
users.should.have.property('length', 0);
done();
});
});
it('should support string "gte" that is satisfied', function (done) {
User.find({order: 'seq', where: { name: { "gte": 'Paul McCartney'}
}}, function (err, users) {
it('should support string "gte" that is satisfied', function(done) {
User.find({order: 'seq', where: {name: {'gte': 'Paul McCartney'},
}}, function(err, users) {
should.not.exist(err);
users.should.have.property('length', 4);
users[0].name.should.equal('Paul McCartney');
@ -374,18 +394,18 @@ describe('basic-querying', function () {
});
});
it('should support string "gt" that is not satisfied', function (done) {
User.find({order: 'seq', where: { name: { "gt": 'xyz' }
}}, function (err, users) {
it('should support string "gt" that is not satisfied', function(done) {
User.find({order: 'seq', where: {name: {'gt': 'xyz'},
}}, function(err, users) {
should.not.exist(err);
users.should.have.property('length', 0);
done();
});
});
it('should support string "gt" that is satisfied', function (done) {
User.find({order: 'seq', where: { name: { "gt": 'Paul McCartney' }
}}, function (err, users) {
it('should support string "gt" that is satisfied', function(done) {
User.find({order: 'seq', where: {name: {'gt': 'Paul McCartney'},
}}, function(err, users) {
should.not.exist(err);
users.should.have.property('length', 3);
users[0].name.should.equal('Ringo Starr');
@ -393,9 +413,9 @@ describe('basic-querying', function () {
});
});
it('should support string "lt" that is satisfied', function (done) {
User.find({order: 'seq', where: { name: { "lt": 'Paul McCartney' }
}}, function (err, users) {
it('should support string "lt" that is satisfied', function(done) {
User.find({order: 'seq', where: {name: {'lt': 'Paul McCartney'},
}}, function(err, users) {
should.not.exist(err);
users.should.have.property('length', 2);
users[0].name.should.equal('John Lennon');
@ -403,9 +423,9 @@ describe('basic-querying', function () {
});
});
it('should support boolean "gte" that is satisfied', function (done) {
User.find({order: 'seq', where: { vip: { "gte": true}
}}, function (err, users) {
it('should support boolean "gte" that is satisfied', function(done) {
User.find({order: 'seq', where: {vip: {'gte': true},
}}, function(err, users) {
should.not.exist(err);
users.should.have.property('length', 3);
users[0].name.should.equal('John Lennon');
@ -413,18 +433,18 @@ describe('basic-querying', function () {
});
});
it('should support boolean "gt" that is not satisfied', function (done) {
User.find({order: 'seq', where: { vip: { "gt": true }
}}, function (err, users) {
it('should support boolean "gt" that is not satisfied', function(done) {
User.find({order: 'seq', where: {vip: {'gt': true},
}}, function(err, users) {
should.not.exist(err);
users.should.have.property('length', 0);
done();
});
});
it('should support boolean "gt" that is satisfied', function (done) {
User.find({order: 'seq', where: { vip: { "gt": false }
}}, function (err, users) {
it('should support boolean "gt" that is satisfied', function(done) {
User.find({order: 'seq', where: {vip: {'gt': false},
}}, function(err, users) {
should.not.exist(err);
users.should.have.property('length', 3);
users[0].name.should.equal('John Lennon');
@ -432,9 +452,9 @@ describe('basic-querying', function () {
});
});
it('should support boolean "lt" that is satisfied', function (done) {
User.find({order: 'seq', where: { vip: { "lt": true }
}}, function (err, users) {
it('should support boolean "lt" that is satisfied', function(done) {
User.find({order: 'seq', where: {vip: {'lt': true},
}}, function(err, users) {
should.not.exist(err);
users.should.have.property('length', 2);
users[0].name.should.equal('George Harrison');
@ -442,17 +462,87 @@ describe('basic-querying', function () {
});
});
it('supports non-empty inq', function() {
// note there is no record with seq=100
return User.find({where: {seq: {inq: [0, 1, 100]}}})
.then(function(result) {
var seqsFound = result.map(function(r) { return r.seq; });
should(seqsFound.sort()).eql([0, 1]);
});
});
it('should only include fields as specified', function (done) {
it('supports empty inq', function() {
return User.find({where: {seq: {inq: []}}})
.then(function(result) {
var seqsFound = result.map(function(r) { return r.seq; });
should(seqsFound).eql([]);
});
});
var itWhenIlikeSupported = connectorCapabilities.ilike ? it : it.skip.bind(it);
itWhenIlikeSupported('should support "like" that is satisfied', function(done) {
User.find({where: {name: {like: 'John'}}}, function(err, users) {
if (err) return done(err);
users.length.should.equal(1);
users[0].name.should.equal('John Lennon');
done();
});
});
itWhenIlikeSupported('should support "like" that is not satisfied', function(done) {
User.find({where: {name: {like: 'Bob'}}}, function(err, users) {
if (err) return done(err);
users.length.should.equal(0);
done();
});
});
var itWhenNilikeSupported = connectorCapabilities.nilike ? it : it.skip.bind(it);
itWhenNilikeSupported('should support "nlike" that is satisfied', function(done) {
User.find({where: {name: {nlike: 'John'}}}, function(err, users) {
if (err) return done(err);
users.length.should.equal(5);
users[0].name.should.equal('Paul McCartney');
done();
});
});
itWhenIlikeSupported('should support "ilike" that is satisfied', function(done) {
User.find({where: {name: {ilike: 'john'}}}, function(err, users) {
if (err) return done(err);
users.length.should.equal(1);
users[0].name.should.equal('John Lennon');
done();
});
});
itWhenIlikeSupported('should support "ilike" that is not satisfied', function(done) {
User.find({where: {name: {ilike: 'bob'}}}, function(err, users) {
if (err) return done(err);
users.length.should.equal(0);
done();
});
});
itWhenNilikeSupported('should support "nilike" that is satisfied', function(done) {
User.find({where: {name: {nilike: 'john'}}}, function(err, users) {
if (err) return done(err);
users.length.should.equal(5);
users[0].name.should.equal('Paul McCartney');
done();
});
});
it('should only include fields as specified', function(done) {
var remaining = 0;
function sample(fields) {
return {
expect: function (arr) {
expect: function(arr) {
remaining++;
User.find({fields: fields}, function (err, users) {
User.find({fields: fields}, function(err, users) {
remaining--;
if (err) return done(err);
@ -462,11 +552,11 @@ describe('basic-querying', function () {
done();
}
users.forEach(function (user) {
users.forEach(function(user) {
var obj = user.toObject();
Object.keys(obj)
.forEach(function (key) {
.forEach(function(key) {
// if the obj has an unexpected value
if (obj[key] !== undefined && arr.indexOf(key) === -1) {
console.log('Given fields:', fields);
@ -477,8 +567,8 @@ describe('basic-querying', function () {
});
});
});
}
}
},
};
}
sample({name: true}).expect(['name']);
@ -489,15 +579,13 @@ describe('basic-querying', function () {
sample(['id']).expect(['id']);
sample(['email']).expect(['email']);
});
});
describe('count', function () {
describe('count', function() {
before(seed);
it('should query total count', function (done) {
User.count(function (err, n) {
it('should query total count', function(done) {
User.count(function(err, n) {
should.not.exist(err);
should.exist(n);
n.should.equal(6);
@ -505,8 +593,8 @@ describe('basic-querying', function () {
});
});
it('should query filtered count', function (done) {
User.count({role: 'lead'}, function (err, n) {
it('should query filtered count', function(done) {
User.count({role: 'lead'}, function(err, n) {
should.not.exist(err);
should.exist(n);
n.should.equal(2);
@ -515,13 +603,12 @@ describe('basic-querying', function () {
});
});
describe('findOne', function () {
describe('findOne', function() {
before(seed);
it('should find first record (default sort by id)', function (done) {
User.all({order: 'id'}, function (err, users) {
User.findOne(function (e, u) {
it('should find first record (default sort by id)', function(done) {
User.all({order: 'id'}, function(err, users) {
User.findOne(function(e, u) {
should.not.exist(e);
should.exist(u);
u.id.toString().should.equal(users[0].id.toString());
@ -530,8 +617,8 @@ describe('basic-querying', function () {
});
});
it('should find first record', function (done) {
User.findOne({order: 'order'}, function (e, u) {
it('should find first record', function(done) {
User.findOne({order: 'order'}, function(e, u) {
should.not.exist(e);
should.exist(u);
u.order.should.equal(1);
@ -540,8 +627,8 @@ describe('basic-querying', function () {
});
});
it('should find last record', function (done) {
User.findOne({order: 'order DESC'}, function (e, u) {
it('should find last record', function(done) {
User.findOne({order: 'order DESC'}, function(e, u) {
should.not.exist(e);
should.exist(u);
u.order.should.equal(6);
@ -550,11 +637,11 @@ describe('basic-querying', function () {
});
});
it('should find last record in filtered set', function (done) {
it('should find last record in filtered set', function(done) {
User.findOne({
where: {role: 'lead'},
order: 'order DESC'
}, function (e, u) {
order: 'order DESC',
}, function(e, u) {
should.not.exist(e);
should.exist(u);
u.order.should.equal(2);
@ -563,25 +650,23 @@ describe('basic-querying', function () {
});
});
it('should work even when find by id', function (done) {
User.findOne(function (e, u) {
User.findOne({where: {id: u.id}}, function (err, user) {
it('should work even when find by id', function(done) {
User.findOne(function(e, u) {
User.findOne({where: {id: u.id}}, function(err, user) {
should.not.exist(err);
should.exist(user);
done();
});
});
});
});
describe('exists', function () {
describe('exists', function() {
before(seed);
it('should check whether record exist', function (done) {
User.findOne(function (e, u) {
User.exists(u.id, function (err, exists) {
it('should check whether record exist', function(done) {
User.findOne(function(e, u) {
User.exists(u.id, function(err, exists) {
should.not.exist(err);
should.exist(exists);
exists.should.be.ok;
@ -590,9 +675,9 @@ describe('basic-querying', function () {
});
});
it('should check whether record not exist', function (done) {
User.destroyAll(function () {
User.exists(42, function (err, exists) {
it('should check whether record not exist', function(done) {
User.destroyAll(function() {
User.exists(42, function(err, exists) {
should.not.exist(err);
exists.should.not.be.ok;
done();
@ -627,9 +712,9 @@ describe.skip('queries', function() {
var db = getSchema();
Todo = db.define('Todo', {
id: false,
content: {type: 'string'}
content: {type: 'string'},
}, {
idInjection: false
idInjection: false,
});
db.automigrate(['Todo'], done);
});
@ -638,7 +723,7 @@ describe.skip('queries', function() {
Todo.create([
{content: 'Buy eggs'},
{content: 'Buy milk'},
{content: 'Buy sausages'}
{content: 'Buy sausages'},
], done);
});
});
@ -737,15 +822,15 @@ describe.skip('queries', function() {
it('should return an error for deleteById/destroyById/removeById',
function(done) {
var aliases = ['deleteById', 'destroyById', 'removeById'];
async.each(aliases, function(alias, cb) {
Todo[alias](1, function(err) {
should.exist(err);
err.message.should.equal(expectedErrMsg);
cb();
var aliases = ['deleteById', 'destroyById', 'removeById'];
async.each(aliases, function(alias, cb) {
Todo[alias](1, function(err) {
should.exist(err);
err.message.should.equal(expectedErrMsg);
cb();
});
}, done);
});
}, done);
});
it('should return an error for instance.save', function(done) {
var todo = new Todo();
@ -798,7 +883,7 @@ function seed(done) {
role: 'lead',
birthday: new Date('1980-12-08'),
order: 2,
vip: true
vip: true,
},
{
seq: 1,
@ -807,18 +892,23 @@ function seed(done) {
role: 'lead',
birthday: new Date('1942-06-18'),
order: 1,
vip: true
vip: true,
},
{seq: 2, name: 'George Harrison', order: 5, vip: false},
{seq: 3, name: 'Ringo Starr', order: 6, vip: false},
{seq: 4, name: 'Pete Best', order: 4},
{seq: 5, name: 'Stuart Sutcliffe', order: 3, vip: true}
{seq: 5, name: 'Stuart Sutcliffe', order: 3, vip: true},
];
async.series([
User.destroyAll.bind(User),
function(cb) {
async.each(beatles, User.create.bind(User), cb);
}
},
], done);
}
function nextAfterDelay(ctx, next) {
var randomTimeoutTrigger = Math.floor(Math.random() * 100);
setTimeout(function() { process.nextTick(next); }, randomTimeoutTrigger);
}

View File

@ -1,3 +1,9 @@
// Copyright IBM Corp. 2013,2016. All Rights Reserved.
// Node module: loopback-datasource-juggler
// This file is licensed under the MIT License.
// License text available at https://opensource.org/licenses/MIT
'use strict';
require('./datatype.test.js');
require('./basic-querying.test.js');
require('./manipulation.test.js');

View File

@ -1,3 +1,9 @@
// Copyright IBM Corp. 2011,2016. All Rights Reserved.
// Node module: loopback-datasource-juggler
// This file is licensed under the MIT License.
// License text available at https://opensource.org/licenses/MIT
'use strict';
var Schema = require('../index').Schema;
var Text = Schema.Text;
@ -15,7 +21,6 @@ function skip(name) {
}
module.exports = function testSchema(exportCasesHere, dataSource) {
batch = exportCasesHere;
schemaName = dataSource.name;
if (dataSource.name.match(/^\/.*\/test\/\.\.$/)) {
@ -23,13 +28,13 @@ module.exports = function testSchema(exportCasesHere, dataSource) {
}
var start;
batch['should connect to database'] = function (test) {
batch['should connect to database'] = function(test) {
start = Date.now();
if (dataSource.connected) return test.done();
dataSource.on('connected', test.done);
};
dataSource.log = function (a) {
dataSource.log = function(a) {
console.log(a);
nbSchemaRequests++;
};
@ -38,7 +43,7 @@ module.exports = function testSchema(exportCasesHere, dataSource) {
testOrm(dataSource);
batch['all tests done'] = function (test) {
batch['all tests done'] = function(test) {
test.done();
process.nextTick(allTestsDone);
};
@ -47,26 +52,25 @@ module.exports = function testSchema(exportCasesHere, dataSource) {
// dataSource.disconnect();
console.log('Test done in %dms\n', Date.now() - start);
}
};
Object.defineProperty(module.exports, 'it', {
writable: true,
enumerable: false,
configurable: true,
value: it
value: it,
});
Object.defineProperty(module.exports, 'skip', {
writable: true,
enumerable: false,
configurable: true,
value: skip
value: skip,
});
function clearAndCreate(model, data, callback) {
var createdItems = [];
model.destroyAll(function () {
model.destroyAll(function() {
nextItem(null, null);
});
@ -90,57 +94,56 @@ function testOrm(dataSource) {
var Post, User, Passport, Log, Dog;
it('should define class', function (test) {
it('should define class', function(test) {
User = dataSource.define('User', {
name: { type: String, index: true },
email: { type: String, index: true },
name: {type: String, index: true},
email: {type: String, index: true},
bio: Text,
approved: Boolean,
joinedAt: Date,
age: Number,
passwd: { type: String, index: true }
passwd: {type: String, index: true},
});
Dog = dataSource.define('Dog', {
name: { type: String, limit: 64, allowNull: false }
name: {type: String, limit: 64, allowNull: false},
});
Log = dataSource.define('Log', {
ownerId: { type: Number, allowNull: true },
name: { type: String, limit: 64, allowNull: false }
ownerId: {type: Number, allowNull: true},
name: {type: String, limit: 64, allowNull: false},
});
Log.belongsTo(Dog, {as: 'owner', foreignKey: 'ownerId'});
dataSource.extendModel('User', {
settings: { type: Schema.JSON },
extra: Object
settings: {type: Schema.JSON},
extra: Object,
});
var newuser = new User({settings: {hey: 'you'}});
test.ok(newuser.settings);
Post = dataSource.define('Post', {
title: { type: String, length: 255, index: true },
subject: { type: String },
content: { type: Text },
date: { type: Date, default: function () {
return new Date
}, index: true },
published: { type: Boolean, default: false, index: true },
title: {type: String, length: 255, index: true},
subject: {type: String},
content: {type: Text},
date: {type: Date, default: function() {
return new Date;
}, index: true},
published: {type: Boolean, default: false, index: true},
likes: [],
related: [RelatedPost]
related: [RelatedPost],
}, {table: 'posts'});
function RelatedPost() {
}
RelatedPost.prototype.someMethod = function () {
RelatedPost.prototype.someMethod = function() {
return this.parent;
};
Post.validateAsync('title', function (err, done) {
Post.validateAsync('title', function(err, done) {
process.nextTick(done);
});
@ -166,7 +169,7 @@ function testOrm(dataSource) {
// post.author(user) -- setter when called with object
Passport = dataSource.define('Passport', {
number: String
number: String,
});
Passport.belongsTo(User, {as: 'owner', foreignKey: 'ownerId'});
@ -183,7 +186,7 @@ function testOrm(dataSource) {
// instance methods
test.ok(user.save instanceof Function);
dataSource.automigrate(function (err) {
dataSource.automigrate(function(err) {
if (err) {
console.log('Error while migrating');
console.log(err);
@ -191,10 +194,9 @@ function testOrm(dataSource) {
test.done();
}
});
});
it('should initialize object properly', function (test) {
it('should initialize object properly', function(test) {
var hw = 'Hello word',
now = Date.now(),
post = new Post({title: hw}),
@ -213,31 +215,31 @@ function testOrm(dataSource) {
test.done();
});
it('should save object', function (test) {
it('should save object', function(test) {
var title = 'Initial title', title2 = 'Hello world',
date = new Date;
Post.create({
title: title,
date: date
}, function (err, obj) {
date: date,
}, function(err, obj) {
test.ok(obj.id, 'Object id should present');
test.equals(obj.title, title);
// test.equals(obj.date, date);
obj.title = title2;
test.ok(obj.propertyChanged('title'), 'Title changed');
obj.save(function (err, obj) {
obj.save(function(err, obj) {
test.equal(obj.title, title2);
test.ok(!obj.propertyChanged('title'));
var p = new Post({title: 1});
p.title = 2;
p.save(function (err, obj) {
p.save(function(err, obj) {
test.ok(!p.propertyChanged('title'));
p.title = 3;
test.ok(p.propertyChanged('title'));
test.equal(p.title_was, 2);
p.save(function () {
p.save(function() {
test.equal(p.title_was, 3);
test.ok(!p.propertyChanged('title'));
test.done();
@ -247,18 +249,18 @@ function testOrm(dataSource) {
});
});
it('should create object with initial data', function (test) {
it('should create object with initial data', function(test) {
var title = 'Initial title',
date = new Date;
Post.create({
title: title,
date: date
}, function (err, obj) {
date: date,
}, function(err, obj) {
test.ok(obj.id);
test.equals(obj.title, title);
test.equals(obj.date, date);
Post.findById(obj.id, function () {
Post.findById(obj.id, function() {
test.equal(obj.title, title);
test.equal(obj.date.toString(), date.toString());
test.done();
@ -266,13 +268,13 @@ function testOrm(dataSource) {
});
});
it('should save only dataSource-defined field in database', function (test) {
Post.create({title: '1602', nonSchemaField: 'some value'}, function (err, post) {
it('should save only dataSource-defined field in database', function(test) {
Post.create({title: '1602', nonSchemaField: 'some value'}, function(err, post) {
test.ok(!post.nonSchemaField);
post.a = 1;
post.save(function () {
post.save(function() {
test.ok(post.a);
post.reload(function (err, psto) {
post.reload(function(err, psto) {
test.ok(!psto.a);
test.done();
});
@ -296,24 +298,25 @@ function testOrm(dataSource) {
});
*/
it('should not re-instantiate object on saving', function (test) {
it('should not re-instantiate object on saving', function(test) {
var title = 'Initial title';
var post = new Post({title: title});
post.save(function (err, savedPost) {
post.save(function(err, savedPost) {
test.strictEqual(post, savedPost);
test.done();
});
});
it('should destroy object', function (test) {
Post.create(function (err, post) {
Post.exists(post.id, function (err, exists) {
it('should destroy object', function(test) {
Post.create(function(err, post) {
Post.exists(post.id, function(err, exists) {
test.ok(exists, 'Object exists');
post.destroy(function () {
Post.exists(post.id, function (err, exists) {
post.destroy(function() {
Post.exists(post.id, function(err, exists) {
if (err) console.log(err);
test.ok(!exists, 'Hey! ORM told me that object exists, but it looks like it doesn\'t. Something went wrong...');
Post.findById(post.id, function (err, obj) {
test.ok(!exists, 'Hey! ORM told me that object exists, but it ' +
'looks like it doesn\'t. Something went wrong...');
Post.findById(post.id, function(err, obj) {
test.equal(obj, null, 'Param obj should be null');
test.done();
});
@ -323,10 +326,10 @@ function testOrm(dataSource) {
});
});
it('should handle virtual attributes', function (test) {
it('should handle virtual attributes', function(test) {
var salt = 's0m3s3cr3t5a1t';
User.setter.passwd = function (password) {
User.setter.passwd = function(password) {
this._passwd = calcHash(password, salt);
};
@ -356,15 +359,15 @@ function testOrm(dataSource) {
// });
// });
it('should update single attribute', function (test) {
Post.create({title: 'title', content: 'content', published: true}, function (err, post) {
it('should update single attribute', function(test) {
Post.create({title: 'title', content: 'content', published: true}, function(err, post) {
post.content = 'New content';
post.updateAttribute('title', 'New title', function () {
post.updateAttribute('title', 'New title', function() {
test.equal(post.title, 'New title');
test.ok(!post.propertyChanged('title'));
test.equal(post.content, 'New content', 'dirty state saved');
test.ok(post.propertyChanged('content'));
post.reload(function (err, post) {
post.reload(function(err, post) {
test.equal(post.title, 'New title');
test.ok(!post.propertyChanged('title'), 'title not changed');
test.equal(post.content, 'content', 'real value turned back');
@ -376,22 +379,22 @@ function testOrm(dataSource) {
});
var countOfposts, countOfpostsFiltered;
it('should fetch collection', function (test) {
Post.all(function (err, posts) {
it('should fetch collection', function(test) {
Post.all(function(err, posts) {
countOfposts = posts.length;
test.ok(countOfposts > 0);
test.ok(posts[0] instanceof Post);
countOfpostsFiltered = posts.filter(function (p) {
countOfpostsFiltered = posts.filter(function(p) {
return p.title === 'title';
}).length;
test.done();
});
});
it('should find records filtered with multiple attributes', function (test) {
it('should find records filtered with multiple attributes', function(test) {
var d = new Date;
Post.create({title: 'title', content: 'content', published: true, date: d}, function (err, post) {
Post.all({where: {title: 'title', date: d, published: true}}, function (err, res) {
Post.create({title: 'title', content: 'content', published: true, date: d}, function(err, post) {
Post.all({where: {title: 'title', date: d, published: true}}, function(err, res) {
test.equals(res.length, 1, 'Filtering Posts returns one post');
test.done();
});
@ -404,7 +407,7 @@ function testOrm(dataSource) {
dataSource.name !== 'neo4j' &&
dataSource.name !== 'cradle'
)
it('relations key is working', function (test) {
it('relations key is working', function(test) {
test.ok(User.relations, 'Relations key should be defined');
test.ok(User.relations.posts, 'posts relation should exist on User');
test.equal(User.relations.posts.type, 'hasMany', 'Type of hasMany relation is hasMany');
@ -421,15 +424,15 @@ function testOrm(dataSource) {
test.done();
});
it('should handle hasMany relationship', function (test) {
User.create(function (err, u) {
it('should handle hasMany relationship', function(test) {
User.create(function(err, u) {
if (err) return console.log(err);
test.ok(u.posts, 'Method defined: posts');
test.ok(u.posts.build, 'Method defined: posts.build');
test.ok(u.posts.create, 'Method defined: posts.create');
u.posts.create(function (err, post) {
u.posts.create(function(err, post) {
if (err) return console.log(err);
u.posts(function (err, posts) {
u.posts(function(err, posts) {
test.equal(posts.pop().id.toString(), post.id.toString());
test.done();
});
@ -437,13 +440,12 @@ function testOrm(dataSource) {
});
});
it('should navigate variations of belongsTo regardless of column name', function (test) {
Dog.create({name: 'theDog'}, function (err, obj) {
it('should navigate variations of belongsTo regardless of column name', function(test) {
Dog.create({name: 'theDog'}, function(err, obj) {
test.ok(obj instanceof Dog);
Log.create({name: 'theLog', ownerId: obj.id}, function (err, obj) {
Log.create({name: 'theLog', ownerId: obj.id}, function(err, obj) {
test.ok(obj instanceof Log);
obj.owner(function (err, obj) {
obj.owner(function(err, obj) {
test.ok(!err, 'Should not have an error.'); // Before cba174b this would be 'Error: Permission denied'
if (err) {
console.log('Found: ' + err);
@ -462,39 +464,37 @@ function testOrm(dataSource) {
});
});
it('hasMany should support additional conditions', function (test) {
User.create(function (e, u) {
u.posts.create({}, function (e, p) {
u.posts({where: {id: p.id}}, function (e, posts) {
it('hasMany should support additional conditions', function(test) {
User.create(function(e, u) {
u.posts.create({}, function(e, p) {
u.posts({where: {id: p.id}}, function(e, posts) {
test.equal(posts.length, 1, 'There should be only 1 post.');
test.done();
});
});
});
});
it('hasMany should be cached', function (test) {
/*eslint-disable*/
it('hasMany should be cached', function(test) {
//User.create(function (e, u) {
// u.posts.create({}, function (e, p) {
// find all posts for a user.
// Finding one post with an existing author associated
Post.all(function (err, posts) {
Post.all(function(err, posts) {
// We try to get the first post with a userId != NULL
for (var i = 0; i < posts.length; i++) {
var post = posts[i];
if (post.userId) {
// We could get the user with belongs to relationship but it is better if there is no interactions.
User.findById(post.userId, function (err, user) {
User.create(function (err, voidUser) {
Post.create({userId: user.id}, function () {
User.findById(post.userId, function(err, user) {
User.create(function(err, voidUser) {
Post.create({ userId: user.id }, function() {
// There can't be any concurrency because we are counting requests
// We are first testing cases when user has posts
user.posts(function (err, data) {
user.posts(function(err, data) {
var nbInitialRequests = nbSchemaRequests;
user.posts(function (err, data2) {
user.posts(function(err, data2) {
test.equal(data.length, 2, 'There should be 2 posts.');
test.equal(data.length, data2.length, 'Posts should be the same, since we are loading on the same object.');
requestsAreCounted && test.equal(nbInitialRequests, nbSchemaRequests, 'There should not be any request because value is cached.');
@ -502,23 +502,23 @@ function testOrm(dataSource) {
if (dataSource.name === 'mongodb') { // for the moment mongodb doesn\'t support additional conditions on hasMany relations (see above)
test.done();
} else {
user.posts({where: {id: data[0].id}}, function (err, data) {
user.posts({ where: { id: data[0].id }}, function(err, data) {
test.equal(data.length, 1, 'There should be only one post.');
requestsAreCounted && test.equal(nbInitialRequests + 1, nbSchemaRequests, 'There should be one additional request since we added conditions.');
user.posts(function (err, data) {
user.posts(function(err, data) {
test.equal(data.length, 2, 'Previous get shouldn\'t have changed cached value though, since there was additional conditions.');
requestsAreCounted && test.equal(nbInitialRequests + 1, nbSchemaRequests, 'There should not be any request because value is cached.');
// We are now testing cases when user doesn't have any post
voidUser.posts(function (err, data) {
voidUser.posts(function(err, data) {
var nbInitialRequests = nbSchemaRequests;
voidUser.posts(function (err, data2) {
voidUser.posts(function(err, data2) {
test.equal(data.length, 0, 'There shouldn\'t be any posts (1/2).');
test.equal(data2.length, 0, 'There shouldn\'t be any posts (2/2).');
requestsAreCounted && test.equal(nbInitialRequests, nbSchemaRequests, 'There should not be any request because value is cached.');
voidUser.posts(true, function (err, data3) {
voidUser.posts(true, function(err, data3) {
test.equal(data3.length, 0, 'There shouldn\'t be any posts.');
requestsAreCounted && test.equal(nbInitialRequests + 1, nbSchemaRequests, 'There should be one additional request since we forced refresh.');
@ -526,14 +526,11 @@ function testOrm(dataSource) {
});
});
});
});
});
}
});
});
});
});
});
@ -541,8 +538,8 @@ function testOrm(dataSource) {
}
}
});
});
/*eslint-enable*/
// it('should handle hasOne relationship', function (test) {
// User.create(function (err, u) {
@ -550,7 +547,7 @@ function testOrm(dataSource) {
// });
// });
it('should support scopes', function (test) {
it('should support scopes', function(test) {
var wait = 2;
test.ok(Post.scope, 'Scope supported');
@ -560,14 +557,14 @@ function testOrm(dataSource) {
var post = Post.published.build();
test.ok(post.published, 'Can build');
test.ok(post.isNewRecord());
Post.published.create(function (err, psto) {
Post.published.create(function(err, psto) {
if (err) return console.log(err);
test.ok(psto.published);
test.ok(!psto.isNewRecord());
done();
});
User.create(function (err, u) {
User.create(function(err, u) {
if (err) return console.log(err);
test.ok(typeof u.posts.published == 'function');
test.ok(u.posts.published._scope.where.published);
@ -581,7 +578,7 @@ function testOrm(dataSource) {
};
});
it('should return type of property', function (test) {
it('should return type of property', function(test) {
test.equal(Post.getPropertyType('title'), 'String');
test.equal(Post.getPropertyType('content'), 'Text');
var p = new Post;
@ -590,25 +587,25 @@ function testOrm(dataSource) {
test.done();
});
it('should handle ORDER clause', function (test) {
it('should handle ORDER clause', function(test) {
var titles = [
{ title: 'Title A', subject: "B" },
{ title: 'Title Z', subject: "A" },
{ title: 'Title M', subject: "C" },
{ title: 'Title A', subject: "A" },
{ title: 'Title B', subject: "A" },
{ title: 'Title C', subject: "D" }
{title: 'Title A', subject: 'B'},
{title: 'Title Z', subject: 'A'},
{title: 'Title M', subject: 'C'},
{title: 'Title A', subject: 'A'},
{title: 'Title B', subject: 'A'},
{title: 'Title C', subject: 'D'},
];
var isRedis = Post.dataSource.name === 'redis';
var dates = isRedis ? [ 5, 9, 0, 17, 10, 9 ] : [
var dates = isRedis ? [5, 9, 0, 17, 10, 9] : [
new Date(1000 * 5),
new Date(1000 * 9),
new Date(1000 * 0),
new Date(1000 * 17),
new Date(1000 * 10),
new Date(1000 * 9)
new Date(1000 * 9),
];
titles.forEach(function (t, i) {
titles.forEach(function(t, i) {
Post.create({title: t.title, subject: t.subject, date: dates[i]}, done);
});
@ -638,10 +635,10 @@ function testOrm(dataSource) {
function doStringTest() {
tests += 1;
Post.all({order: 'title'}, function (err, posts) {
Post.all({order: 'title'}, function(err, posts) {
if (err) console.log(err);
test.equal(posts.length, 6);
titles.sort(compare).forEach(function (t, i) {
titles.sort(compare).forEach(function(t, i) {
if (posts[i]) test.equal(posts[i].title, t.title);
});
finished();
@ -650,10 +647,10 @@ function testOrm(dataSource) {
function doNumberTest() {
tests += 1;
Post.all({order: 'date'}, function (err, posts) {
Post.all({order: 'date'}, function(err, posts) {
if (err) console.log(err);
test.equal(posts.length, 6);
dates.sort(numerically).forEach(function (d, i) {
dates.sort(numerically).forEach(function(d, i) {
if (posts[i])
test.equal(posts[i].date.toString(), d.toString(), 'doNumberTest');
});
@ -663,11 +660,11 @@ function testOrm(dataSource) {
function doFilterAndSortTest() {
tests += 1;
Post.all({where: {date: new Date(1000 * 9)}, order: 'title', limit: 3}, function (err, posts) {
Post.all({where: {date: new Date(1000 * 9)}, order: 'title', limit: 3}, function(err, posts) {
if (err) console.log(err);
console.log(posts.length);
test.equal(posts.length, 2, 'Exactly 2 posts returned by query');
[ 'Title C', 'Title Z' ].forEach(function (t, i) {
['Title C', 'Title Z'].forEach(function(t, i) {
if (posts[i]) {
test.equal(posts[i].title, t, 'doFilterAndSortTest');
}
@ -678,10 +675,10 @@ function testOrm(dataSource) {
function doFilterAndSortReverseTest() {
tests += 1;
Post.all({where: {date: new Date(1000 * 9)}, order: 'title DESC', limit: 3}, function (err, posts) {
Post.all({where: {date: new Date(1000 * 9)}, order: 'title DESC', limit: 3}, function(err, posts) {
if (err) console.log(err);
test.equal(posts.length, 2, 'Exactly 2 posts returned by query');
[ 'Title Z', 'Title C' ].forEach(function (t, i) {
['Title Z', 'Title C'].forEach(function(t, i) {
if (posts[i]) {
test.equal(posts[i].title, t, 'doFilterAndSortReverseTest');
}
@ -692,28 +689,28 @@ function testOrm(dataSource) {
function doMultipleSortTest() {
tests += 1;
Post.all({order: "title ASC, subject ASC"}, function (err, posts) {
Post.all({order: 'title ASC, subject ASC'}, function(err, posts) {
if (err) console.log(err);
test.equal(posts.length, 6);
test.equal(posts[0].title, "Title A");
test.equal(posts[0].subject, "A");
test.equal(posts[1].title, "Title A");
test.equal(posts[1].subject, "B");
test.equal(posts[5].title, "Title Z");
test.equal(posts[0].title, 'Title A');
test.equal(posts[0].subject, 'A');
test.equal(posts[1].title, 'Title A');
test.equal(posts[1].subject, 'B');
test.equal(posts[5].title, 'Title Z');
finished();
});
}
function doMultipleReverseSortTest() {
tests += 1;
Post.all({order: "title ASC, subject DESC"}, function (err, posts) {
Post.all({order: 'title ASC, subject DESC'}, function(err, posts) {
if (err) console.log(err);
test.equal(posts.length, 6);
test.equal(posts[0].title, "Title A");
test.equal(posts[0].subject, "B");
test.equal(posts[1].title, "Title A");
test.equal(posts[1].subject, "A");
test.equal(posts[5].title, "Title Z");
test.equal(posts[0].title, 'Title A');
test.equal(posts[0].subject, 'B');
test.equal(posts[1].title, 'Title A');
test.equal(posts[1].subject, 'A');
test.equal(posts[5].title, 'Title Z');
finished();
});
}
@ -731,7 +728,6 @@ function testOrm(dataSource) {
function numerically(a, b) {
return a - b;
}
});
// if (
@ -872,7 +868,7 @@ function testOrm(dataSource) {
// }
// });
it('should handle order clause with direction', function (test) {
it('should handle order clause with direction', function(test) {
var wait = 0;
var emails = [
'john@hcompany.com',
@ -881,10 +877,10 @@ function testOrm(dataSource) {
'tin@hcompany.com',
'mike@hcompany.com',
'susan@hcompany.com',
'test@hcompany.com'
'test@hcompany.com',
];
User.destroyAll(function () {
emails.forEach(function (email) {
User.destroyAll(function() {
emails.forEach(function(email) {
wait += 1;
User.create({email: email, name: 'Nick'}, done);
});
@ -892,7 +888,7 @@ function testOrm(dataSource) {
var tests = 2;
function done() {
process.nextTick(function () {
process.nextTick(function() {
if (--wait === 0) {
doSortTest();
doReverseSortTest();
@ -901,9 +897,9 @@ function testOrm(dataSource) {
}
function doSortTest() {
User.all({order: 'email ASC', where: {name: 'Nick'}}, function (err, users) {
User.all({order: 'email ASC', where: {name: 'Nick'}}, function(err, users) {
var _emails = emails.sort();
users.forEach(function (user, i) {
users.forEach(function(user, i) {
test.equal(_emails[i], user.email, 'ASC sorting');
});
testDone();
@ -911,9 +907,9 @@ function testOrm(dataSource) {
}
function doReverseSortTest() {
User.all({order: 'email DESC', where: {name: 'Nick'}}, function (err, users) {
User.all({order: 'email DESC', where: {name: 'Nick'}}, function(err, users) {
var _emails = emails.sort().reverse();
users.forEach(function (user, i) {
users.forEach(function(user, i) {
test.equal(_emails[i], user.email, 'DESC sorting');
});
testDone();
@ -925,12 +921,12 @@ function testOrm(dataSource) {
}
});
it('should return id in find result even after updateAttributes', function (test) {
Post.create(function (err, post) {
it('should return id in find result even after updateAttributes', function(test) {
Post.create(function(err, post) {
var id = post.id;
test.ok(post.published === false);
post.updateAttributes({title: 'hey', published: true}, function () {
Post.find(id, function (err, post) {
post.updateAttributes({title: 'hey', published: true}, function() {
Post.find(id, function(err, post) {
test.ok(!!post.published, 'Update boolean field');
test.ok(post.id);
test.done();
@ -939,7 +935,7 @@ function testOrm(dataSource) {
});
});
it('should handle belongsTo correctly', function (test) {
it('should handle belongsTo correctly', function(test) {
var passport = new Passport({ownerId: 16});
// sync getter
test.equal(passport.owner(), 16);
@ -949,18 +945,18 @@ function testOrm(dataSource) {
test.done();
});
it('should query one record', function (test) {
it('should query one record', function(test) {
test.expect(4);
Post.findOne(function (err, post) {
Post.findOne(function(err, post) {
test.ok(post && post.id);
Post.findOne({ where: { title: 'hey' } }, function (err, post) {
Post.findOne({where: {title: 'hey'}}, function(err, post) {
if (err) {
console.log(err);
return test.done();
}
test.equal(post && post.constructor.modelName, 'Post');
test.equal(post && post.title, 'hey');
Post.findOne({ where: { title: 'not exists' } }, function (err, post) {
Post.findOne({where: {title: 'not exists'}}, function(err, post) {
test.ok(post === null);
test.done();
});
@ -1018,13 +1014,13 @@ function testOrm(dataSource) {
// });
if (dataSource.name !== 'mongoose' && dataSource.name !== 'neo4j')
it('should update or create record', function (test) {
it('should update or create record', function(test) {
var newData = {
id: 1,
title: 'New title (really new)',
content: 'Some example content (updated)'
content: 'Some example content (updated)',
};
Post.updateOrCreate(newData, function (err, updatedPost) {
Post.updateOrCreate(newData, function(err, updatedPost) {
if (err) throw err;
test.ok(updatedPost);
if (!updatedPost) throw Error('No post!');
@ -1035,7 +1031,7 @@ function testOrm(dataSource) {
test.equal(newData.title, updatedPost.toObject().title);
test.equal(newData.content, updatedPost.toObject().content);
Post.findById(updatedPost.id, function (err, post) {
Post.findById(updatedPost.id, function(err, post) {
if (err) throw err;
if (!post) throw Error('No post!');
if (dataSource.name !== 'mongodb') {
@ -1043,10 +1039,10 @@ function testOrm(dataSource) {
}
test.equal(newData.title, post.toObject().title);
test.equal(newData.content, post.toObject().content);
Post.updateOrCreate({id: 100001, title: 'hey'}, function (err, post) {
Post.updateOrCreate({id: 100001, title: 'hey'}, function(err, post) {
if (dataSource.name !== 'mongodb') test.equal(post.id, 100001);
test.equal(post.title, 'hey');
Post.findById(post.id, function (err, post) {
Post.findById(post.id, function(err, post) {
if (!post) throw Error('No post!');
test.done();
});
@ -1055,25 +1051,25 @@ function testOrm(dataSource) {
});
});
it('should work with custom setters and getters', function (test) {
it('should work with custom setters and getters', function(test) {
User.dataSource.defineForeignKey('User', 'passwd');
User.setter.passwd = function (pass) {
User.setter.passwd = function(pass) {
this._passwd = pass + 'salt';
};
var u = new User({passwd: 'qwerty'});
test.equal(u.passwd, 'qwertysalt');
u.save(function (err, user) {
User.findById(user.id, function (err, user) {
u.save(function(err, user) {
User.findById(user.id, function(err, user) {
test.ok(user !== u);
test.equal(user.passwd, 'qwertysalt');
User.all({where: {passwd: 'qwertysalt'}}, function (err, users) {
User.all({where: {passwd: 'qwertysalt'}}, function(err, users) {
test.ok(users[0] !== user);
test.equal(users[0].passwd, 'qwertysalt');
User.create({passwd: 'asalat'}, function (err, usr) {
User.create({passwd: 'asalat'}, function(err, usr) {
test.equal(usr.passwd, 'asalatsalt');
User.upsert({passwd: 'heyman'}, function (err, us) {
User.upsert({passwd: 'heyman'}, function(err, us) {
test.equal(us.passwd, 'heymansalt');
User.findById(us.id, function (err, user) {
User.findById(us.id, function(err, user) {
test.equal(user.passwd, 'heymansalt');
test.done();
});
@ -1084,18 +1080,18 @@ function testOrm(dataSource) {
});
});
it('should work with typed and untyped nested collections', function (test) {
it('should work with typed and untyped nested collections', function(test) {
var post = new Post;
var like = post.likes.push({foo: 'bar'});
test.equal(like.constructor.name, 'ListItem');
var related = post.related.push({hello: 'world'});
test.ok(related.someMethod);
post.save(function (err, p) {
post.save(function(err, p) {
test.equal(p.likes.nextid, 2);
p.likes.push({second: 2});
p.likes.push({third: 3});
p.save(function (err) {
Post.findById(p.id, function (err, pp) {
p.save(function(err) {
Post.findById(p.id, function(err, pp) {
test.equal(pp.likes.length, 3);
test.ok(pp.likes[3].third);
test.ok(pp.likes[2].second);
@ -1107,8 +1103,8 @@ function testOrm(dataSource) {
test.equal(pp.likes.length, 1);
test.ok(!pp.likes[1]);
test.ok(pp.likes[3]);
pp.save(function () {
Post.findById(p.id, function (err, pp) {
pp.save(function() {
Post.findById(p.id, function(err, pp) {
test.equal(pp.likes.length, 1);
test.ok(!pp.likes[1]);
test.ok(pp.likes[3]);
@ -1120,13 +1116,13 @@ function testOrm(dataSource) {
});
});
it('should find or create', function (test) {
it('should find or create', function(test) {
var email = 'some email ' + Math.random();
User.findOrCreate({where: {email: email}}, function (err, u, created) {
User.findOrCreate({where: {email: email}}, function(err, u, created) {
test.ok(u);
test.ok(!u.age);
test.ok(created);
User.findOrCreate({where: {email: email}}, {age: 21}, function (err, u2, created) {
User.findOrCreate({where: {email: email}}, {age: 21}, function(err, u2, created) {
test.equals(u.id.toString(), u2.id.toString(), 'Same user ids');
test.ok(!u2.age);
test.ok(!created);
@ -1134,5 +1130,4 @@ function testOrm(dataSource) {
});
});
});
}

View File

@ -1,11 +1,16 @@
// Copyright IBM Corp. 2015,2016. All Rights Reserved.
// Node module: loopback-datasource-juggler
// This file is licensed under the MIT License.
// License text available at https://opensource.org/licenses/MIT
'use strict';
// This test written in mocha+should.js
var should = require('./init.js');
var async = require('async');
var db, User, options, filter;
describe('crud-with-options', function () {
before(function (done) {
describe('crud-with-options', function() {
before(function(done) {
db = getSchema();
User = db.define('User', {
seq: {type: Number, index: true},
@ -14,50 +19,49 @@ describe('crud-with-options', function () {
birthday: {type: Date, index: true},
role: {type: String, index: true},
order: {type: Number, index: true, sort: true},
vip: {type: Boolean}
vip: {type: Boolean},
meta: {type: Object},
});
options = {};
filter = {fields: ['name', 'id']};
db.automigrate(['User'], done);
});
describe('findById', function () {
before(function (done) {
describe('findById', function() {
before(function(done) {
User.destroyAll(done);
});
it('should allow findById(id, options, cb)', function (done) {
User.findById(1, options, function (err, u) {
it('should allow findById(id, options, cb)', function(done) {
User.findById(1, options, function(err, u) {
should.not.exist(u);
should.not.exist(err);
done();
});
});
it('should allow findById(id, filter, cb)', function (done) {
User.findById(1, filter, function (err, u) {
it('should allow findById(id, filter, cb)', function(done) {
User.findById(1, filter, function(err, u) {
should.not.exist(u);
should.not.exist(err);
done();
});
});
it('should allow findById(id)', function () {
it('should allow findById(id)', function() {
User.findById(1);
});
it('should allow findById(id, filter)', function () {
it('should allow findById(id, filter)', function() {
User.findById(1, filter);
});
it('should allow findById(id, options)', function () {
it('should allow findById(id, options)', function() {
User.findById(1, options);
});
it('should allow findById(id, filter, options)', function () {
it('should allow findById(id, filter, options)', function() {
User.findById(1, filter, options);
});
@ -67,7 +71,7 @@ describe('crud-with-options', function () {
User.findById(1, '123', function(err, u) {
});
}).should.throw('The filter argument must be an object');
done();
done();
});
it('should throw when invalid options are provided for findById',
@ -181,19 +185,17 @@ describe('crud-with-options', function () {
done(err);
});
});
});
describe('findByIds', function () {
describe('findByIds', function() {
before(function(done) {
var people = [
{ id: 1, name: 'a', vip: true },
{ id: 2, name: 'b' },
{ id: 3, name: 'c' },
{ id: 4, name: 'd', vip: true },
{ id: 5, name: 'e' },
{ id: 6, name: 'f' }
{id: 1, name: 'a', vip: true},
{id: 2, name: 'b'},
{id: 3, name: 'c'},
{id: 4, name: 'd', vip: true},
{id: 5, name: 'e'},
{id: 6, name: 'f'},
];
// Use automigrate so that serial keys are 1-6
db.automigrate(['User'], function(err) {
@ -203,8 +205,8 @@ describe('crud-with-options', function () {
});
});
it('should allow findByIds(ids, cb)', function (done) {
User.findByIds([3, 2, 1], function (err, users) {
it('should allow findByIds(ids, cb)', function(done) {
User.findByIds([3, 2, 1], function(err, users) {
should.exist(users);
should.not.exist(err);
var names = users.map(function(u) { return u.name; });
@ -216,7 +218,7 @@ describe('crud-with-options', function () {
it('should allow findByIds(ids, filter, options, cb)',
function(done) {
User.findByIds([4, 3, 2, 1],
{ where: { vip: true } }, options, function(err, users) {
{where: {vip: true}}, options, function(err, users) {
should.exist(users);
should.not.exist(err);
var names = users.map(function(u) {
@ -226,11 +228,9 @@ describe('crud-with-options', function () {
done();
});
});
});
describe('find', function () {
describe('find', function() {
before(seed);
it('should allow find(cb)', function(done) {
@ -260,6 +260,15 @@ describe('crud-with-options', function () {
});
});
it('should not throw for nested properties for ANY or Object type', function(done) {
User.find({where: {'meta.thisPropertyNotDefined': true}}, function(err, users) {
should.not.exists(err);
should.exists(users);
users.should.have.lengthOf(0);
done();
});
});
it('should allow find(filter, options)', function() {
User.find({limit: 3}, options);
});
@ -298,15 +307,13 @@ describe('crud-with-options', function () {
User.find({limit: 3}, {}, 'invalid cb');
}).should.throw('The cb argument must be a function');
});
});
describe('count', function () {
describe('count', function() {
before(seed);
it('should allow count(cb)', function (done) {
User.count(function (err, n) {
it('should allow count(cb)', function(done) {
User.count(function(err, n) {
should.not.exist(err);
should.exist(n);
n.should.equal(6);
@ -314,8 +321,8 @@ describe('crud-with-options', function () {
});
});
it('should allow count(where, cb)', function (done) {
User.count({role: 'lead'}, function (err, n) {
it('should allow count(where, cb)', function(done) {
User.count({role: 'lead'}, function(err, n) {
should.not.exist(err);
should.exist(n);
n.should.equal(2);
@ -323,24 +330,22 @@ describe('crud-with-options', function () {
});
});
it('should allow count(where, options, cb)', function (done) {
User.count({role: 'lead'}, options, function (err, n) {
it('should allow count(where, options, cb)', function(done) {
User.count({role: 'lead'}, options, function(err, n) {
should.not.exist(err);
should.exist(n);
n.should.equal(2);
done();
});
});
});
describe('findOne', function () {
describe('findOne', function() {
before(seed);
it('should allow findOne(cb)', function (done) {
User.find({order: 'id'}, function (err, users) {
User.findOne(function (e, u) {
it('should allow findOne(cb)', function(done) {
User.find({order: 'id'}, function(err, users) {
User.findOne(function(e, u) {
should.not.exist(e);
should.exist(u);
u.id.toString().should.equal(users[0].id.toString());
@ -349,8 +354,8 @@ describe('crud-with-options', function () {
});
});
it('should allow findOne(filter, options, cb)', function (done) {
User.findOne({order: 'order'}, options, function (e, u) {
it('should allow findOne(filter, options, cb)', function(done) {
User.findOne({order: 'order'}, options, function(e, u) {
should.not.exist(e);
should.exist(u);
u.order.should.equal(1);
@ -359,8 +364,8 @@ describe('crud-with-options', function () {
});
});
it('should allow findOne(filter, cb)', function (done) {
User.findOne({order: 'order'}, function (e, u) {
it('should allow findOne(filter, cb)', function(done) {
User.findOne({order: 'order'}, function(e, u) {
should.not.exist(e);
should.exist(u);
u.order.should.equal(1);
@ -369,8 +374,8 @@ describe('crud-with-options', function () {
});
});
it('should allow trailing undefined args', function (done) {
User.findOne({order: 'order'}, function (e, u) {
it('should allow trailing undefined args', function(done) {
User.findOne({order: 'order'}, function(e, u) {
should.not.exist(e);
should.exist(u);
u.order.should.equal(1);
@ -378,16 +383,14 @@ describe('crud-with-options', function () {
done();
}, undefined);
});
});
describe('exists', function () {
describe('exists', function() {
before(seed);
it('should allow exists(id, cb)', function (done) {
User.findOne(function (e, u) {
User.exists(u.id, function (err, exists) {
it('should allow exists(id, cb)', function(done) {
User.findOne(function(e, u) {
User.exists(u.id, function(err, exists) {
should.not.exist(err);
should.exist(exists);
exists.should.be.ok;
@ -396,22 +399,20 @@ describe('crud-with-options', function () {
});
});
it('should allow exists(id, options, cb)', function (done) {
User.destroyAll(function () {
User.exists(42, options, function (err, exists) {
it('should allow exists(id, options, cb)', function(done) {
User.destroyAll(function() {
User.exists(42, options, function(err, exists) {
should.not.exist(err);
exists.should.not.be.ok;
done();
});
});
});
});
describe('save', function () {
it('should allow save(options, cb)', function (done) {
var options = { foo: 'bar' };
describe('save', function() {
it('should allow save(options, cb)', function(done) {
var options = {foo: 'bar'};
var opts;
User.observe('after save', function(ctx, next) {
@ -426,20 +427,18 @@ describe('crud-with-options', function () {
done();
});
});
});
describe('destroyAll with options', function () {
describe('destroyAll with options', function() {
beforeEach(seed);
it('should allow destroyAll(where, options, cb)', function (done) {
User.destroyAll({name: 'John Lennon'}, options, function (err) {
it('should allow destroyAll(where, options, cb)', function(done) {
User.destroyAll({name: 'John Lennon'}, options, function(err) {
should.not.exist(err);
User.find({where: {name: 'John Lennon'}}, function (err, data) {
User.find({where: {name: 'John Lennon'}}, function(err, data) {
should.not.exist(err);
data.length.should.equal(0);
User.find({where: {name: 'Paul McCartney'}}, function (err, data) {
User.find({where: {name: 'Paul McCartney'}}, function(err, data) {
should.not.exist(err);
data.length.should.equal(1);
done();
@ -448,13 +447,13 @@ describe('crud-with-options', function () {
});
});
it('should allow destroyAll(where, cb)', function (done) {
User.destroyAll({name: 'John Lennon'}, function (err) {
it('should allow destroyAll(where, cb)', function(done) {
User.destroyAll({name: 'John Lennon'}, function(err) {
should.not.exist(err);
User.find({where: {name: 'John Lennon'}}, function (err, data) {
User.find({where: {name: 'John Lennon'}}, function(err, data) {
should.not.exist(err);
data.length.should.equal(0);
User.find({where: {name: 'Paul McCartney'}}, function (err, data) {
User.find({where: {name: 'Paul McCartney'}}, function(err, data) {
should.not.exist(err);
data.length.should.equal(1);
done();
@ -463,13 +462,13 @@ describe('crud-with-options', function () {
});
});
it('should allow destroyAll(cb)', function (done) {
User.destroyAll(function (err) {
it('should allow destroyAll(cb)', function(done) {
User.destroyAll(function(err) {
should.not.exist(err);
User.find({where: {name: 'John Lennon'}}, function (err, data) {
User.find({where: {name: 'John Lennon'}}, function(err, data) {
should.not.exist(err);
data.length.should.equal(0);
User.find({where: {name: 'Paul McCartney'}}, function (err, data) {
User.find({where: {name: 'Paul McCartney'}}, function(err, data) {
should.not.exist(err);
data.length.should.equal(0);
done();
@ -477,20 +476,18 @@ describe('crud-with-options', function () {
});
});
});
});
describe('updateAll ', function () {
describe('updateAll ', function() {
beforeEach(seed);
it('should allow updateAll(where, data, cb)', function (done) {
User.update({name: 'John Lennon'}, {name: 'John Smith'}, function (err) {
it('should allow updateAll(where, data, cb)', function(done) {
User.update({name: 'John Lennon'}, {name: 'John Smith'}, function(err) {
should.not.exist(err);
User.find({where: {name: 'John Lennon'}}, function (err, data) {
User.find({where: {name: 'John Lennon'}}, function(err, data) {
should.not.exist(err);
data.length.should.equal(0);
User.find({where: {name: 'John Smith'}}, function (err, data) {
User.find({where: {name: 'John Smith'}}, function(err, data) {
should.not.exist(err);
data.length.should.equal(1);
done();
@ -515,12 +512,12 @@ describe('crud-with-options', function () {
});
});
it('should allow updateAll(data, cb)', function (done) {
User.update({name: 'John Smith'}, function () {
User.find({where: {name: 'John Lennon'}}, function (err, data) {
it('should allow updateAll(data, cb)', function(done) {
User.update({name: 'John Smith'}, function() {
User.find({where: {name: 'John Lennon'}}, function(err, data) {
should.not.exist(err);
data.length.should.equal(0);
User.find({where: {name: 'John Smith'}}, function (err, data) {
User.find({where: {name: 'John Smith'}}, function(err, data) {
should.not.exist(err);
data.length.should.equal(6);
done();
@ -528,9 +525,70 @@ describe('crud-with-options', function () {
});
});
});
});
});
describe('upsertWithWhere', function() {
beforeEach(seed);
it('rejects upsertWithWhere (options,cb)', function(done) {
try {
User.upsertWithWhere({}, function(err) {
if (err) return done(err);
});
} catch (ex) {
ex.message.should.equal('The data argument must be an object');
done();
}
});
it('rejects upsertWithWhere (cb)', function(done) {
try {
User.upsertWithWhere(function(err) {
if (err) return done(err);
});
} catch (ex) {
ex.message.should.equal('The where argument must be an object');
done();
}
});
it('allows upsertWithWhere by accepting where,data and cb as arguments', function(done) {
User.upsertWithWhere({name: 'John Lennon'}, {name: 'John Smith'}, function(err) {
if (err) return done(err);
User.find({where: {name: 'John Lennon'}}, function(err, data) {
if (err) return done(err);
data.length.should.equal(0);
User.find({where: {name: 'John Smith'}}, function(err, data) {
if (err) return done(err);
data.length.should.equal(1);
data[0].name.should.equal('John Smith');
data[0].email.should.equal('john@b3atl3s.co.uk');
data[0].role.should.equal('lead');
data[0].order.should.equal(2);
data[0].vip.should.equal(true);
done();
});
});
});
});
it('allows upsertWithWhere by accepting where, data, options, and cb as arguments', function(done) {
options = {};
User.upsertWithWhere({name: 'John Lennon'}, {name: 'John Smith'}, options, function(err) {
if (err) return done(err);
User.find({where: {name: 'John Smith'}}, function(err, data) {
if (err) return done(err);
data.length.should.equal(1);
data[0].name.should.equal('John Smith');
data[0].seq.should.equal(0);
data[0].email.should.equal('john@b3atl3s.co.uk');
data[0].role.should.equal('lead');
data[0].order.should.equal(2);
data[0].vip.should.equal(true);
done();
});
});
});
});
function seed(done) {
@ -542,7 +600,7 @@ function seed(done) {
role: 'lead',
birthday: new Date('1980-12-08'),
order: 2,
vip: true
vip: true,
},
{
seq: 1,
@ -551,18 +609,18 @@ function seed(done) {
role: 'lead',
birthday: new Date('1942-06-18'),
order: 1,
vip: true
vip: true,
},
{seq: 2, name: 'George Harrison', order: 5, vip: false},
{seq: 3, name: 'Ringo Starr', order: 6, vip: false},
{seq: 4, name: 'Pete Best', order: 4},
{seq: 5, name: 'Stuart Sutcliffe', order: 3, vip: true}
{seq: 5, name: 'Stuart Sutcliffe', order: 3, vip: true},
];
async.series([
User.destroyAll.bind(User),
function(cb) {
async.each(beatles, User.create.bind(User), cb);
}
},
], done);
}

47
test/datasource.test.js Normal file
View File

@ -0,0 +1,47 @@
// Copyright IBM Corp. 2016. All Rights Reserved.
// Node module: loopback-datasource-juggler
// This file is licensed under the MIT License.
// License text available at https://opensource.org/licenses/MIT
'use strict';
var should = require('./init.js');
var DataSource = require('../lib/datasource.js').DataSource;
describe('DataSource', function() {
it('reports helpful error when connector init throws', function() {
var throwingConnector = {
name: 'loopback-connector-throwing',
initialize: function(ds, cb) {
throw new Error('expected test error');
},
};
(function() {
// this is what LoopBack does
return new DataSource({
name: 'dsname',
connector: throwingConnector,
});
}).should.throw(/loopback-connector-throwing/);
});
it('reports helpful error when connector init via short name throws', function() {
(function() {
// this is what LoopBack does
return new DataSource({
name: 'dsname',
connector: 'throwing',
});
}).should.throw(/expected test error/);
});
it('reports helpful error when connector init via long name throws', function() {
(function() {
// this is what LoopBack does
return new DataSource({
name: 'dsname',
connector: 'loopback-connector-throwing',
});
}).should.throw(/expected test error/);
});
});

View File

@ -1,13 +1,18 @@
// Copyright IBM Corp. 2013,2016. All Rights Reserved.
// Node module: loopback-datasource-juggler
// This file is licensed under the MIT License.
// License text available at https://opensource.org/licenses/MIT
'use strict';
// This test written in mocha+should.js
var should = require('./init.js');
var db, Model;
describe('datatypes', function () {
before(function (done) {
describe('datatypes', function() {
before(function(done) {
db = getSchema();
Nested = db.define('Nested', {});
var Nested = db.define('Nested', {});
Model = db.define('Model', {
str: String,
@ -16,65 +21,71 @@ describe('datatypes', function () {
bool: Boolean,
list: {type: [String]},
arr: Array,
nested: Nested
nested: Nested,
});
db.automigrate(['Model'], done);
});
it('should return 400 when property of type array is set to string value',
function (done) {
function(done) {
var myModel = db.define('myModel', {
list: { type: ['object'] }
list: {type: ['object']},
});
(function(){
myModel.create({ list: 'This string will crash the server' });
}).should.throw({ statusCode: 400 });
done();
});
myModel.create({list: 'This string will crash the server'}, function(err) {
(err.statusCode).should.equal(400);
done();
});
});
it('should return 400 when property of type array is set to object value',
function (done) {
function(done) {
var myModel = db.define('myModel', {
list: { type: ['object'] }
list: {type: ['object']},
});
(function(){
myModel.create({ list: { key: 'This string will crash the server' } });
}).should.throw({ statusCode: 400 });
myModel.create({list: {key: 'This string will crash the server'}}, function(err) {
(err.statusCode).should.equal(400);
done();
});
});
done();
});
it('throws an error when property of type Date is set to an invalid value',
function() {
var myModel = db.define('myModel', {
date: {type: Date},
});
myModel.create({date: 'invalid'}, function(err) {
(err.message).should.equal('Invalid date: invalid');
});
});
it('should keep types when get read data from db', function(done) {
var d = new Date, id;
it('should keep types when get read data from db', function (done) {
var d = new Date;
var id;
Model.create({
str: 'hello', date: d, num: '3', bool: 1, list: ['test'], arr: [1, 'str']
}, function (err, m) {
str: 'hello', date: d, num: '3', bool: 1, list: ['test'], arr: [1, 'str'],
}, function(err, m) {
should.not.exists(err);
should.exist(m && m.id);
should(m.str).be.type('string');
should(m.num).be.type('number');
should(m.bool).be.type('boolean');
m.str.should.be.type('string');
m.num.should.be.type('number');
m.bool.should.be.type('boolean');
m.list[0].should.be.equal('test');
m.arr[0].should.be.equal(1);
m.arr[1].should.be.equal('str');
m.date.should.be.an.instanceOf(Date);
m.date.toString().should.equal(d.toString())
id = m.id;
testFind(testAll);
});
function testFind(next) {
Model.findById(id, function (err, m) {
Model.findById(id, function(err, m) {
should.not.exist(err);
should.exist(m);
should(m.str).be.type('string');
should(m.num).be.type('number');
should(m.bool).be.type('boolean');
m.str.should.be.type('string');
m.num.should.be.type('number');
m.bool.should.be.type('boolean');
m.list[0].should.be.equal('test');
m.arr[0].should.be.equal(1);
m.arr[1].should.be.equal('str');
@ -85,18 +96,20 @@ describe('datatypes', function () {
}
function testAll() {
Model.findOne(function (err, m) {
Model.findOne(function(err, m) {
should.not.exist(err);
should.exist(m);
should(m.str).be.type('string');
should(m.num).be.type('number');
should(m.bool).be.type('boolean');
m.str.should.be.type('string');
m.num.should.be.type('number');
m.bool.should.be.type('boolean');
m.date.should.be.an.instanceOf(Date);
m.date.toString().should.equal(d.toString(), 'Time must match');
done();
});
}
});
it('should respect data types when updating attributes', function (done) {
it('should respect data types when updating attributes', function(done) {
var d = new Date, id;
Model.create({
@ -105,11 +118,11 @@ describe('datatypes', function () {
should.exist(m && m.id);
// sanity check initial types
should(m.str).be.type('string');
should(m.num).be.type('number');
should(m.bool).be.type('boolean');
m.str.should.be.type('string');
m.num.should.be.type('number');
m.bool.should.be.type('boolean');
id = m.id;
testDataInDB(function () {
testDataInDB(function() {
testUpdate(function() {
testDataInDB(done);
});
@ -122,20 +135,20 @@ describe('datatypes', function () {
// update using updateAttributes
m.updateAttributes({
id: m.id, num: '10'
}, function (err, m) {
id: m.id, num: '10',
}, function(err, m) {
should.not.exist(err);
m.num.should.be.type('number');
done();
});
});
}
function testDataInDB(done) {
// verify that the value stored in the db is still an object
function cb(err, data) {
should.exist(data);
should(data.num).be.type('number');
data.num.should.be.type('number');
done();
}
@ -148,18 +161,18 @@ describe('datatypes', function () {
});
it('should not coerce nested objects into ModelConstructor types', function() {
var coerced = Model._coerce({ nested: { foo: 'bar' } });
coerced.nested.constructor.name.should.equal('Object');
var coerced = Model._coerce({nested: {foo: 'bar'}});
coerced.nested.constructor.name.should.equal('Object');
});
it('rejects array value converted to NaN for a required property',
function(done) {
db = getSchema();
Model = db.define('RequiredNumber', {
num: { type: Number, required: true }
num: {type: Number, required: true},
});
db.automigrate(['Model'], function () {
Model.create({ num: [1,2,3] }, function(err, inst) {
db.automigrate(['Model'], function() {
Model.create({num: [1, 2, 3]}, function(err, inst) {
should.exist(err);
err.should.have.property('name').equal('ValidationError');
done();
@ -173,11 +186,11 @@ describe('datatypes', function () {
TestModel = db.define(
'TestModel',
{
desc: { type: String, required: false },
stars: { type: Number, required: false }
desc: {type: String, required: false},
stars: {type: Number, required: false},
},
{
persistUndefinedAsNull: true
persistUndefinedAsNull: true,
});
isStrict = TestModel.definition.settings.strict;
@ -186,39 +199,35 @@ describe('datatypes', function () {
});
it('should set missing optional properties to null', function(done) {
TestModel.create({ name: 'a-test-name' }, function(err, created) {
var EXPECTED = {desc: null, stars: null};
TestModel.create({name: 'a-test-name'}, function(err, created) {
if (err) return done(err);
created.should.have.property('desc', null);
created.should.have.property('stars', null);
created.should.have.properties(EXPECTED);
TestModel.findById(created.id, function(err, found) {
if (err) return done(err);
created.should.have.property('desc', null);
created.should.have.property('stars', null);
found.should.have.properties(EXPECTED);
done();
});
});
});
it('should convert property value undefined to null', function(done) {
it('should convert property value undefined to null', function(done) {
var EXPECTED = {desc: null, extra: null};
if (isStrict) {
// SQL-based connectors don't support dynamic properties
delete EXPECTED.extra;
}
var data ={ desc: undefined, extra: undefined };
var data = {desc: undefined, extra: undefined};
TestModel.create(data, function(err, created) {
if (err) return done(err);
created.should.have.property('desc', null);
created.should.have.property('stars', null);
created.should.have.properties(EXPECTED);
TestModel.findById(created.id, function(err, found) {
if (err) return done(err);
created.should.have.property('desc', null);
created.should.have.property('stars', null);
found.should.have.properties(EXPECTED);
done();
});
});
@ -238,8 +247,8 @@ describe('datatypes', function () {
inst.toObject().should.have.property('stars', null);
});
// TODO: There is a bug for this; please refer to https://github.com/strongloop/loopback-connector-redis/issues/9
it.skip('should convert undefined to null on save', function(done) {
it('should convert undefined to null on save', function(done) {
var EXPECTED = {desc: null, stars: null, extra: null, dx: null};
if (isStrict) {
// SQL-based connectors don't support dynamic properties
delete EXPECTED.extra;
@ -248,7 +257,6 @@ describe('datatypes', function () {
TestModel.create({}, function(err, created) {
if (err) return done(err);
created.desc = undefined; // Note: this is may be a no-op
created.unsetAttribute('stars');
created.extra = undefined;
@ -257,8 +265,8 @@ describe('datatypes', function () {
created.save(function(err, saved) {
if (err) return done(err);
saved.should.have.property('extra', null);
saved.should.have.property('dx', null);
created.should.have.properties(EXPECTED);
saved.should.have.properties(EXPECTED);
function cb(err, found) {
if (err) return done(err);
@ -292,11 +300,9 @@ describe('datatypes', function () {
inst.extra = undefined;
inst.__data.dx = undefined;
var result = inst.toObject(false);
result.should.have.property('desc', null);
result.should.have.property('stars', null);
result.should.have.property('extra', null);
result.should.have.property('dx', null);
inst.toObject(false).should.have.properties({
desc: null, stars: null, extra: null, dx: null,
});
});
});
});

View File

@ -1,3 +1,9 @@
// Copyright IBM Corp. 2014,2016. All Rights Reserved.
// Node module: loopback-datasource-juggler
// This file is licensed under the MIT License.
// License text available at https://opensource.org/licenses/MIT
'use strict';
// This test written in mocha+should.js
var should = require('./init.js');
var async = require('async');
@ -41,80 +47,82 @@ var setupProducts = function(ids, done) {
ids.widgetB = inst.id;
next();
});
}
},
], done);
};
describe('default scope', function () {
before(function (done) {
describe('default scope', function() {
before(function(done) {
db = getSchema();
Category = db.define('Category', {
name: String
name: String,
});
Product = db.define('Product', {
name: String,
kind: String,
description: String,
active: { type: Boolean, default: true }
active: {type: Boolean, default: true},
}, {
scope: { order: 'name' },
scopes: { active: { where: { active: true } } }
scope: {order: 'name'},
scopes: {active: {where: {active: true}}},
});
Product.lookupModel = function(data) {
var m = this.dataSource.models[data.kind];
if (m.base === this) return m;
return this;
};
Tool = db.define('Tool', Product.definition.properties, {
base: 'Product',
scope: { where: { kind: 'Tool' }, order: 'name' },
scopes: { active: { where: { active: true } } },
mongodb: { collection: 'Product' },
memory: { collection: 'Product' }
});
Widget = db.define('Widget', Product.definition.properties, {
base: 'Product',
properties: { kind: 'Widget' },
scope: { where: { kind: 'Widget' }, order: 'name' },
scopes: { active: { where: { active: true } } },
mongodb: { collection: 'Product' },
memory: { collection: 'Product' }
base: 'Product',
scope: {where: {kind: 'Tool'}, order: 'name'},
scopes: {active: {where: {active: true}}},
arangodb: {collection: 'Product'},
mongodb: {collection: 'Product'},
memory: {collection: 'Product'},
});
Person = db.define('Person', { name: String }, {
scope: { include: 'things' }
Widget = db.define('Widget', Product.definition.properties, {
base: 'Product',
properties: {kind: 'Widget'},
scope: {where: {kind: 'Widget'}, order: 'name'},
scopes: {active: {where: {active: true}}},
arangodb: {collection: 'Product'},
mongodb: {collection: 'Product'},
memory: {collection: 'Product'},
});
Person = db.define('Person', {name: String}, {
scope: {include: 'things'},
});
// inst is only valid for instance methods
// like save, updateAttributes
var scopeFn = function(target, inst) {
return { where: { kind: this.modelName } };
return {where: {kind: this.modelName}};
};
var propertiesFn = function(target, inst) {
return { kind: this.modelName };
return {kind: this.modelName};
};
Thing = db.define('Thing', Product.definition.properties, {
base: 'Product',
attributes: propertiesFn,
scope: scopeFn,
mongodb: { collection: 'Product' },
memory: { collection: 'Product' }
base: 'Product',
attributes: propertiesFn,
scope: scopeFn,
arangodb: {collection: 'Product'},
mongodb: {collection: 'Product'},
memory: {collection: 'Product'},
});
Category.hasMany(Product);
Category.hasMany(Tool, {scope: {order: 'name DESC'}});
Category.hasMany(Widget);
Category.hasMany(Thing);
Product.belongsTo(Category);
Tool.belongsTo(Category);
Widget.belongsTo(Category);
@ -122,29 +130,28 @@ describe('default scope', function () {
Person.hasMany(Thing);
Thing.belongsTo(Person);
db.automigrate(done);
});
describe('manipulation', function() {
var ids = {};
before(function(done) {
db.automigrate(done);
});
it('should return a scoped instance', function() {
var p = new Tool({name: 'Product A', kind:'ignored'});
var p = new Tool({name: 'Product A', kind: 'ignored'});
p.name.should.equal('Product A');
p.kind.should.equal('Tool');
p.setAttributes({ kind: 'ignored' });
p.setAttributes({kind: 'ignored'});
p.kind.should.equal('Tool');
p.setAttribute('kind', 'other'); // currently not enforced
p.kind.should.equal('other');
});
it('should create a scoped instance - tool', function(done) {
Tool.create({name: 'Product A', kind: 'ignored'}, function(err, p) {
should.not.exist(err);
@ -154,7 +161,7 @@ describe('default scope', function () {
done();
});
});
it('should create a scoped instance - widget', function(done) {
Widget.create({name: 'Product B', kind: 'ignored'}, function(err, p) {
should.not.exist(err);
@ -164,7 +171,7 @@ describe('default scope', function () {
done();
});
});
it('should update a scoped instance - updateAttributes', function(done) {
Tool.findById(ids.productA, function(err, p) {
p.updateAttributes({description: 'A thing...', kind: 'ingored'}, function(err, inst) {
@ -176,7 +183,7 @@ describe('default scope', function () {
});
});
});
it('should update a scoped instance - save', function(done) {
Tool.findById(ids.productA, function(err, p) {
p.description = 'Something...';
@ -193,28 +200,26 @@ describe('default scope', function () {
});
});
});
it('should update a scoped instance - updateOrCreate', function(done) {
var data = {id: ids.productA, description: 'Anything...', kind: 'ingored'};
Tool.updateOrCreate(data, function(err, p) {
should.not.exist(err);
p.name.should.equal('Product A');
p.kind.should.equal('Tool');
p.description.should.equal('Anything...');
done();
should.not.exist(err);
p.name.should.equal('Product A');
p.kind.should.equal('Tool');
p.description.should.equal('Anything...');
done();
});
});
});
describe('findById', function() {
var ids = {};
before(function (done) {
before(function(done) {
db.automigrate(setupProducts.bind(null, ids, done));
});
it('should apply default scope', function(done) {
Product.findById(ids.toolA, function(err, inst) {
should.not.exist(err);
@ -223,7 +228,7 @@ describe('default scope', function () {
done();
});
});
it('should apply default scope - tool', function(done) {
Tool.findById(ids.toolA, function(err, inst) {
should.not.exist(err);
@ -231,7 +236,7 @@ describe('default scope', function () {
done();
});
});
it('should apply default scope (no match)', function(done) {
Widget.findById(ids.toolA, function(err, inst) {
should.not.exist(err);
@ -239,17 +244,15 @@ describe('default scope', function () {
done();
});
});
});
describe('find', function() {
var ids = {};
before(function (done) {
before(function(done) {
db.automigrate(setupProducts.bind(null, ids, done));
});
it('should apply default scope - order', function(done) {
Product.find(function(err, products) {
should.not.exist(err);
@ -259,19 +262,19 @@ describe('default scope', function () {
products[2].name.should.equal('Widget A');
products[3].name.should.equal('Widget B');
products[4].name.should.equal('Widget Z');
products[0].should.be.instanceof(Product);
products[0].should.be.instanceof(Tool);
products[2].should.be.instanceof(Product);
products[2].should.be.instanceof(Widget);
done();
});
});
it('should apply default scope - order override', function(done) {
Product.find({ order: 'name DESC' }, function(err, products) {
Product.find({order: 'name DESC'}, function(err, products) {
should.not.exist(err);
products.should.have.length(5);
products[0].name.should.equal('Widget Z');
@ -282,7 +285,7 @@ describe('default scope', function () {
done();
});
});
it('should apply default scope - tool', function(done) {
Tool.find(function(err, products) {
should.not.exist(err);
@ -292,9 +295,9 @@ describe('default scope', function () {
done();
});
});
it('should apply default scope - where (widget)', function(done) {
Widget.find({ where: { active: true } }, function(err, products) {
Widget.find({where: {active: true}}, function(err, products) {
should.not.exist(err);
products.should.have.length(2);
products[0].name.should.equal('Widget A');
@ -302,9 +305,9 @@ describe('default scope', function () {
done();
});
});
it('should apply default scope - order (widget)', function(done) {
Widget.find({ order: 'name DESC' }, function(err, products) {
Widget.find({order: 'name DESC'}, function(err, products) {
should.not.exist(err);
products.should.have.length(3);
products[0].name.should.equal('Widget Z');
@ -313,17 +316,15 @@ describe('default scope', function () {
done();
});
});
});
describe('exists', function() {
var ids = {};
before(function (done) {
before(function(done) {
db.automigrate(setupProducts.bind(null, ids, done));
});
it('should apply default scope', function(done) {
Product.exists(ids.widgetA, function(err, exists) {
should.not.exist(err);
@ -331,7 +332,7 @@ describe('default scope', function () {
done();
});
});
it('should apply default scope - tool', function(done) {
Tool.exists(ids.toolZ, function(err, exists) {
should.not.exist(err);
@ -339,7 +340,7 @@ describe('default scope', function () {
done();
});
});
it('should apply default scope - widget', function(done) {
Widget.exists(ids.widgetA, function(err, exists) {
should.not.exist(err);
@ -347,7 +348,7 @@ describe('default scope', function () {
done();
});
});
it('should apply default scope - tool (no match)', function(done) {
Tool.exists(ids.widgetA, function(err, exists) {
should.not.exist(err);
@ -355,7 +356,7 @@ describe('default scope', function () {
done();
});
});
it('should apply default scope - widget (no match)', function(done) {
Widget.exists(ids.toolZ, function(err, exists) {
should.not.exist(err);
@ -363,17 +364,15 @@ describe('default scope', function () {
done();
});
});
});
describe('count', function() {
var ids = {};
before(function (done) {
before(function(done) {
db.automigrate(setupProducts.bind(null, ids, done));
});
it('should apply default scope - order', function(done) {
Product.count(function(err, count) {
should.not.exist(err);
@ -381,7 +380,7 @@ describe('default scope', function () {
done();
});
});
it('should apply default scope - tool', function(done) {
Tool.count(function(err, count) {
should.not.exist(err);
@ -389,7 +388,7 @@ describe('default scope', function () {
done();
});
});
it('should apply default scope - widget', function(done) {
Widget.count(function(err, count) {
should.not.exist(err);
@ -397,7 +396,7 @@ describe('default scope', function () {
done();
});
});
it('should apply default scope - where', function(done) {
Widget.count({name: 'Widget Z'}, function(err, count) {
should.not.exist(err);
@ -405,7 +404,7 @@ describe('default scope', function () {
done();
});
});
it('should apply default scope - no match', function(done) {
Tool.count({name: 'Widget Z'}, function(err, count) {
should.not.exist(err);
@ -413,13 +412,11 @@ describe('default scope', function () {
done();
});
});
});
describe('removeById', function() {
var ids = {};
function isDeleted(id, done) {
Product.exists(id, function(err, exists) {
should.not.exist(err);
@ -427,25 +424,25 @@ describe('default scope', function () {
done();
});
};
before(function (done) {
before(function(done) {
db.automigrate(setupProducts.bind(null, ids, done));
});
it('should apply default scope', function(done) {
Product.removeById(ids.widgetZ, function(err) {
should.not.exist(err);
isDeleted(ids.widgetZ, done);
});
});
it('should apply default scope - tool', function(done) {
Tool.removeById(ids.toolA, function(err) {
should.not.exist(err);
isDeleted(ids.toolA, done);
});
});
it('should apply default scope - no match', function(done) {
Tool.removeById(ids.widgetA, function(err) {
should.not.exist(err);
@ -456,14 +453,14 @@ describe('default scope', function () {
});
});
});
it('should apply default scope - widget', function(done) {
Widget.removeById(ids.widgetA, function(err) {
should.not.exist(err);
isDeleted(ids.widgetA, done);
});
});
it('should apply default scope - verify', function(done) {
Product.find(function(err, products) {
should.not.exist(err);
@ -473,21 +470,19 @@ describe('default scope', function () {
done();
});
});
});
describe('update', function() {
var ids = {};
before(function (done) {
before(function(done) {
db.automigrate(setupProducts.bind(null, ids, done));
});
it('should apply default scope', function(done) {
Widget.update({active: false},{active: true, kind: 'ignored'}, function(err) {
Widget.update({active: false}, {active: true, kind: 'ignored'}, function(err) {
should.not.exist(err);
Widget.find({where: { active: true }}, function(err, products) {
Widget.find({where: {active: true}}, function(err, products) {
should.not.exist(err);
products.should.have.length(3);
products[0].name.should.equal('Widget A');
@ -497,9 +492,9 @@ describe('default scope', function () {
});
});
});
it('should apply default scope - no match', function(done) {
Tool.update({name: 'Widget A'},{name: 'Ignored'}, function(err) {
Tool.update({name: 'Widget A'}, {name: 'Ignored'}, function(err) {
should.not.exist(err);
Product.findById(ids.widgetA, function(err, product) {
should.not.exist(err);
@ -508,7 +503,7 @@ describe('default scope', function () {
});
});
});
it('should have updated within scope', function(done) {
Product.find({where: {active: true}}, function(err, products) {
should.not.exist(err);
@ -520,17 +515,15 @@ describe('default scope', function () {
done();
});
});
});
describe('remove', function() {
var ids = {};
before(function (done) {
before(function(done) {
db.automigrate(setupProducts.bind(null, ids, done));
});
it('should apply default scope - custom where', function(done) {
Widget.remove({name: 'Widget A'}, function(err) {
should.not.exist(err);
@ -544,7 +537,7 @@ describe('default scope', function () {
});
});
});
it('should apply default scope - custom where (no match)', function(done) {
Tool.remove({name: 'Widget Z'}, function(err) {
should.not.exist(err);
@ -558,7 +551,7 @@ describe('default scope', function () {
});
});
});
it('should apply default scope - deleteAll', function(done) {
Tool.deleteAll(function(err) {
should.not.exist(err);
@ -570,7 +563,7 @@ describe('default scope', function () {
});
});
});
it('should create a scoped instance - tool', function(done) {
Tool.create({name: 'Tool B'}, function(err, p) {
should.not.exist(err);
@ -583,7 +576,7 @@ describe('default scope', function () {
});
});
});
it('should apply default scope - destroyAll', function(done) {
Widget.destroyAll(function(err) {
should.not.exist(err);
@ -594,17 +587,15 @@ describe('default scope', function () {
});
});
});
});
describe('scopes', function() {
var ids = {};
before(function (done) {
before(function(done) {
db.automigrate(setupProducts.bind(null, ids, done));
});
it('should merge with default scope', function(done) {
Product.active(function(err, products) {
should.not.exist(err);
@ -615,7 +606,7 @@ describe('default scope', function () {
done();
});
});
it('should merge with default scope - tool', function(done) {
Tool.active(function(err, products) {
should.not.exist(err);
@ -624,7 +615,7 @@ describe('default scope', function () {
done();
});
});
it('should merge with default scope - widget', function(done) {
Widget.active(function(err, products) {
should.not.exist(err);
@ -634,31 +625,29 @@ describe('default scope', function () {
done();
});
});
});
describe('scope function', function() {
before(function(done) {
db.automigrate(done);
});
it('should create a scoped instance - widget', function(done) {
Widget.create({name: 'Product', kind:'ignored'}, function(err, p) {
Widget.create({name: 'Product', kind: 'ignored'}, function(err, p) {
p.name.should.equal('Product');
p.kind.should.equal('Widget');
done();
});
});
it('should create a scoped instance - thing', function(done) {
Thing.create({name: 'Product', kind:'ignored'}, function(err, p) {
Thing.create({name: 'Product', kind: 'ignored'}, function(err, p) {
p.name.should.equal('Product');
p.kind.should.equal('Thing');
done();
});
});
it('should find a scoped instance - widget', function(done) {
Widget.findOne({where: {name: 'Product'}}, function(err, p) {
p.name.should.equal('Product');
@ -666,7 +655,7 @@ describe('default scope', function () {
done();
});
});
it('should find a scoped instance - thing', function(done) {
Thing.findOne({where: {name: 'Product'}}, function(err, p) {
p.name.should.equal('Product');
@ -674,30 +663,28 @@ describe('default scope', function () {
done();
});
});
it('should find a scoped instance - thing', function(done) {
Product.find({where: {name: 'Product'}}, function(err, products) {
products.should.have.length(2);
products[0].name.should.equal('Product');
products[1].name.should.equal('Product');
var kinds = products.map(function(p) { return p.kind; })
var kinds = products.map(function(p) { return p.kind; });
kinds.sort();
kinds.should.eql(['Thing', 'Widget']);
done();
});
});
});
describe('relations', function() {
var ids = {};
before(function (done) {
before(function(done) {
db.automigrate(done);
});
before(function (done) {
before(function(done) {
Category.create({name: 'Category A'}, function(err, cat) {
ids.categoryA = cat.id;
async.series([
@ -712,11 +699,11 @@ describe('default scope', function () {
},
function(next) {
cat.things.create({name: 'Thing A'}, next);
}
},
], done);
});
});
it('should apply default scope - products', function(done) {
Category.findById(ids.categoryA, function(err, cat) {
should.not.exist(err);
@ -727,21 +714,21 @@ describe('default scope', function () {
products[1].name.should.equal('Tool A');
products[2].name.should.equal('Widget A');
products[3].name.should.equal('Widget B');
products[0].should.be.instanceof(Product);
products[0].should.be.instanceof(Thing);
products[1].should.be.instanceof(Product);
products[1].should.be.instanceof(Tool);
products[2].should.be.instanceof(Product);
products[2].should.be.instanceof(Widget);
done();
});
});
});
it('should apply default scope - widgets', function(done) {
Category.findById(ids.categoryA, function(err, cat) {
should.not.exist(err);
@ -758,7 +745,7 @@ describe('default scope', function () {
});
});
});
it('should apply default scope - tools', function(done) {
Category.findById(ids.categoryA, function(err, cat) {
should.not.exist(err);
@ -774,7 +761,7 @@ describe('default scope', function () {
});
});
});
it('should apply default scope - things', function(done) {
Category.findById(ids.categoryA, function(err, cat) {
should.not.exist(err);
@ -790,13 +777,13 @@ describe('default scope', function () {
});
});
});
it('should create related item with default scope', function(done) {
Category.findById(ids.categoryA, function(err, cat) {
cat.tools.create({name: 'Tool B'}, done);
});
});
it('should use relation scope order', function(done) {
Category.findById(ids.categoryA, function(err, cat) {
should.not.exist(err);
@ -809,18 +796,16 @@ describe('default scope', function () {
});
});
});
});
describe('with include option', function() {
before(function (done) {
before(function(done) {
db.automigrate(done);
});
before(function (done) {
Person.create({ id: 1, name: 'Person A' }, function(err, person) {
person.things.create({ name: 'Thing A' }, done);
before(function(done) {
Person.create({id: 1, name: 'Person A'}, function(err, person) {
person.things.create({name: 'Thing A'}, done);
});
});
@ -835,7 +820,5 @@ describe('default scope', function () {
done();
});
});
});
});

View File

@ -1,47 +1,53 @@
// Copyright IBM Corp. 2013,2016. All Rights Reserved.
// Node module: loopback-datasource-juggler
// This file is licensed under the MIT License.
// License text available at https://opensource.org/licenses/MIT
'use strict';
// This test written in mocha+should.js
var should = require('./init.js');
var db = getSchema();
describe('defaults', function () {
describe('defaults', function() {
var Server;
before(function () {
before(function() {
Server = db.define('Server', {
host: String,
port: {type: Number, default: 80},
createdAt: {type: Date, default: '$now'}
createdAt: {type: Date, default: '$now'},
});
});
it('should apply defaults on new', function () {
it('should apply defaults on new', function() {
var s = new Server;
s.port.should.equal(80);
});
it('should apply defaults on create', function (done) {
Server.create(function (err, s) {
it('should apply defaults on create', function(done) {
Server.create(function(err, s) {
s.port.should.equal(80);
done();
});
});
it('should apply defaults on read', function (done) {
it('should apply defaults on read', function(done) {
db.defineProperty('Server', 'host', {
type: String,
default: 'localhost'
default: 'localhost',
});
Server.all(function (err, servers) {
Server.all(function(err, servers) {
(new String('localhost')).should.equal(servers[0].host);
done();
});
});
it('should ignore defaults with limited fields', function (done) {
Server.create({ host: 'localhost', port: 8080 }, function(err, s) {
it('should ignore defaults with limited fields', function(done) {
Server.create({host: 'localhost', port: 8080}, function(err, s) {
should.not.exist(err);
s.port.should.equal(8080);
Server.find({ fields: ['host'] }, function (err, servers) {
Server.find({fields: ['host']}, function(err, servers) {
servers[0].host.should.equal('localhost');
servers[0].should.have.property('host');
servers[0].should.have.property('port', undefined);
@ -50,17 +56,17 @@ describe('defaults', function () {
});
});
it('should apply defaults in upsert create', function (done) {
Server.upsert({port: 8181 }, function(err, server) {
it('should apply defaults in upsert create', function(done) {
Server.upsert({port: 8181}, function(err, server) {
should.not.exist(err);
should.exist(server.createdAt);
done();
});
});
it('should preserve defaults in upsert update', function (done) {
it('should preserve defaults in upsert update', function(done) {
Server.findOne({}, function(err, server) {
Server.upsert({id:server.id, port: 1337 }, function(err, s) {
Server.upsert({id: server.id, port: 1337}, function(err, s) {
should.not.exist(err);
(Number(1337)).should.equal(s.port);
server.createdAt.should.eql(s.createdAt);
@ -68,5 +74,4 @@ describe('defaults', function () {
});
});
});
});

View File

@ -1,3 +1,9 @@
// Copyright IBM Corp. 2015,2016. All Rights Reserved.
// Node module: loopback-datasource-juggler
// This file is licensed under the MIT License.
// License text available at https://opensource.org/licenses/MIT
'use strict';
var jdb = require('../');
var DataSource = jdb.DataSource;
var should = require('./init.js');
@ -26,7 +32,7 @@ describe('Memory connector with mocked discovery', function() {
dataLength: 20,
dataPrecision: null,
dataScale: null,
nullable: 0
nullable: 0,
},
{
owner: 'STRONGLOOP',
@ -36,7 +42,7 @@ describe('Memory connector with mocked discovery', function() {
dataLength: 20,
dataPrecision: null,
dataScale: null,
nullable: 0
nullable: 0,
},
{
owner: 'STRONGLOOP',
@ -46,7 +52,7 @@ describe('Memory connector with mocked discovery', function() {
dataLength: null,
dataPrecision: 10,
dataScale: 0,
nullable: 1
nullable: 1,
},
{
owner: 'STRONGLOOP',
@ -56,7 +62,7 @@ describe('Memory connector with mocked discovery', function() {
dataLength: null,
dataPrecision: 10,
dataScale: 0,
nullable: 1
nullable: 1,
}];
ds.discoverModelProperties = function(modelName, options, cb) {
@ -83,7 +89,7 @@ describe('Memory connector with mocked discovery', function() {
nameMapper: function(type, name) {
// Convert all names to lower case
return name.toLowerCase();
}
},
}, function(err, schemas) {
if (err) return done(err);
schemas.should.have.property('STRONGLOOP.INVENTORY');
@ -113,8 +119,8 @@ describe('Memory connector with mocked discovery', function() {
var models = {
inventory: {
product: {type: 'string'},
location: {type: 'string'}
}
location: {type: 'string'},
},
};
ds.connector.discoverSchemas = function(modelName, options, cb) {
process.nextTick(function() {
@ -133,8 +139,8 @@ describe('Memory connector with mocked discovery', function() {
var models = {
inventory: {
product: {type: 'string'},
location: {type: 'string'}
}
location: {type: 'string'},
},
};
ds.connector.discoverSchemas = function(modelName, options, cb) {
process.nextTick(function() {
@ -169,9 +175,9 @@ describe('Memory connector with mocked discovery', function() {
.catch(function(err) {
done(err);
});
});
});
describe('discoverSchema', function(){
describe('discoverSchema', function() {
var models;
var schema;
before(function() {
@ -179,7 +185,7 @@ describe('Memory connector with mocked discovery', function() {
name: 'Inventory',
options: {
idInjection: false,
memory: { schema: 'STRONGLOOP', table: 'INVENTORY' }
memory: {schema: 'STRONGLOOP', table: 'INVENTORY'},
},
properties: {
available: {
@ -190,12 +196,12 @@ describe('Memory connector with mocked discovery', function() {
dataPrecision: 10,
dataScale: 0,
dataType: 'int',
nullable: 1
nullable: 1,
},
precision: 10,
required: false,
scale: 0,
type: undefined
type: undefined,
},
locationId: {
length: 20,
@ -205,12 +211,12 @@ describe('Memory connector with mocked discovery', function() {
dataPrecision: null,
dataScale: null,
dataType: 'varchar',
nullable: 0
nullable: 0,
},
precision: null,
required: true,
scale: null,
type: undefined
type: undefined,
},
productId: {
length: 20,
@ -220,12 +226,12 @@ describe('Memory connector with mocked discovery', function() {
dataPrecision: null,
dataScale: null,
dataType: 'varchar',
nullable: 0
nullable: 0,
},
precision: null,
required: true,
scale: null,
type: undefined
type: undefined,
},
total: {
length: null,
@ -235,15 +241,15 @@ describe('Memory connector with mocked discovery', function() {
dataPrecision: 10,
dataScale: 0,
dataType: 'int',
nullable: 1
nullable: 1,
},
precision: 10,
required: false,
scale: 0,
type: undefined
}
}
} ;
type: undefined,
},
},
};
});
it('should discover schema using `discoverSchema`', function(done) {
@ -270,16 +276,16 @@ describe('Memory connector with mocked discovery', function() {
schemas.should.be.eql(schema);
done();
})
.catch(function(err){
.catch(function(err) {
done(err);
});
});
});
});
describe('discoverModelDefinitions', function(){
describe('discoverModelDefinitions', function() {
var ds;
before(function(){
before(function() {
ds = new DataSource({connector: 'memory'});
var models = [{type: 'table', name: 'CUSTOMER', owner: 'STRONGLOOP'},
@ -302,7 +308,7 @@ describe('discoverModelDefinitions', function(){
});
tableNames.should.be.eql(
["CUSTOMER", "INVENTORY", "LOCATION"]
['CUSTOMER', 'INVENTORY', 'LOCATION']
);
done();
});
@ -317,7 +323,7 @@ describe('discoverModelDefinitions', function(){
});
tableNames.should.be.eql(
["CUSTOMER", "INVENTORY", "LOCATION"]
['CUSTOMER', 'INVENTORY', 'LOCATION']
);
done();
};
@ -333,20 +339,20 @@ describe('discoverModelDefinitions', function(){
});
tableNames.should.be.eql(
["CUSTOMER", "INVENTORY", "LOCATION"]
['CUSTOMER', 'INVENTORY', 'LOCATION']
);
done();
})
.catch(function(err){
.catch(function(err) {
done(err);
});
});
});
describe('discoverModelProperties', function(){
describe('discoverModelProperties', function() {
var ds;
var modelProperties;
before(function(){
before(function() {
ds = new DataSource({connector: 'memory'});
modelProperties = [{
@ -357,38 +363,38 @@ describe('discoverModelProperties', function(){
dataLength: 20,
dataPrecision: null,
dataScale: null,
nullable: 0
nullable: 0,
},
{
owner: 'STRONGLOOP',
tableName: 'INVENTORY',
columnName: 'LOCATION_ID',
dataType: 'varchar',
dataLength: 20,
dataPrecision: null,
dataScale: null,
nullable: 0
},
{
owner: 'STRONGLOOP',
tableName: 'INVENTORY',
columnName: 'AVAILABLE',
dataType: 'int',
dataLength: null,
dataPrecision: 10,
dataScale: 0,
nullable: 1
},
{
owner: 'STRONGLOOP',
tableName: 'INVENTORY',
columnName: 'TOTAL',
dataType: 'int',
dataLength: null,
dataPrecision: 10,
dataScale: 0,
nullable: 1
}];
{
owner: 'STRONGLOOP',
tableName: 'INVENTORY',
columnName: 'LOCATION_ID',
dataType: 'varchar',
dataLength: 20,
dataPrecision: null,
dataScale: null,
nullable: 0,
},
{
owner: 'STRONGLOOP',
tableName: 'INVENTORY',
columnName: 'AVAILABLE',
dataType: 'int',
dataLength: null,
dataPrecision: 10,
dataScale: 0,
nullable: 1,
},
{
owner: 'STRONGLOOP',
tableName: 'INVENTORY',
columnName: 'TOTAL',
dataType: 'int',
dataLength: null,
dataPrecision: 10,
dataScale: 0,
nullable: 1,
}];
ds.connector.discoverModelProperties = function(modelName, options, cb) {
process.nextTick(function() {
@ -399,11 +405,11 @@ describe('discoverModelProperties', function(){
it('should callback function, passed as options parameter', function(done) {
var options = function(err, schemas) {
if (err) return done(err);
if (err) return done(err);
schemas.should.be.eql(modelProperties);
done();
};
schemas.should.be.eql(modelProperties);
done();
};
ds.discoverModelProperties('INVENTORY', options);
});
@ -423,32 +429,32 @@ describe('discoverModelProperties', function(){
schemas.should.be.eql(modelProperties);
done();
})
.catch(function(err){
.catch(function(err) {
done(err);
});
});
});
describe('discoverPrimaryKeys', function(){
describe('discoverPrimaryKeys', function() {
var ds;
var modelProperties;
before(function(){
var modelProperties, primaryKeys;
before(function() {
ds = new DataSource({connector: 'memory'});
primaryKeys = [
{
{
owner: 'STRONGLOOP',
tableName: 'INVENTORY',
columnName: 'PRODUCT_ID',
keySeq: 1,
pkName: 'ID_PK'
},
{
pkName: 'ID_PK',
},
{
owner: 'STRONGLOOP',
tableName: 'INVENTORY',
columnName: 'LOCATION_ID',
keySeq: 2,
pkName: 'ID_PK'
pkName: 'ID_PK',
}];
ds.connector.discoverPrimaryKeys = function(modelName, options, cb) {
@ -483,19 +489,19 @@ describe('discoverPrimaryKeys', function(){
modelPrimaryKeys.should.be.eql(primaryKeys);
done();
})
.catch(function(err){
.catch(function(err) {
done(err);
});
});
});
describe('discoverForeignKeys', function(){
describe('discoverForeignKeys', function() {
var ds;
var modelProperties;
before(function(){
var modelProperties, foreignKeys;
before(function() {
ds = new DataSource({connector: 'memory'});
foreignKeys = [{
foreignKeys = [{
fkOwner: 'STRONGLOOP',
fkName: 'PRODUCT_FK',
fkTableName: 'INVENTORY',
@ -504,7 +510,7 @@ describe('discoverForeignKeys', function(){
pkOwner: 'STRONGLOOP',
pkName: 'PRODUCT_PK',
pkTableName: 'PRODUCT',
pkColumnName: 'ID'
pkColumnName: 'ID',
}];
ds.connector.discoverForeignKeys = function(modelName, options, cb) {
@ -540,16 +546,16 @@ describe('discoverForeignKeys', function(){
modelForeignKeys.should.be.eql(foreignKeys);
done();
})
.catch(function(err){
.catch(function(err) {
done(err);
});
});
});
describe('discoverExportedForeignKeys', function(){
describe('discoverExportedForeignKeys', function() {
var ds;
var modelProperties;
before(function(){
var modelProperties, exportedForeignKeys;
before(function() {
ds = new DataSource({connector: 'memory'});
exportedForeignKeys = [{
@ -561,7 +567,7 @@ describe('discoverExportedForeignKeys', function(){
pkName: 'PRODUCT_PK',
pkOwner: 'STRONGLOOP',
pkTableName: 'PRODUCT',
pkColumnName: 'ID'
pkColumnName: 'ID',
}];
ds.connector.discoverExportedForeignKeys = function(modelName, options, cb) {
@ -591,13 +597,14 @@ describe('discoverExportedForeignKeys', function(){
ds.discoverExportedForeignKeys('INVENTORY', options);
});
it('should discover foreign key definitions using `discoverExportedForeignKeys` - promise variant', function(done) {
it('should discover foreign key definitions using ' +
'`discoverExportedForeignKeys` - promise variant', function(done) {
ds.discoverExportedForeignKeys('INVENTORY', {})
.then(function(modelForeignKeys) {
modelForeignKeys.should.be.eql(exportedForeignKeys);
done();
})
.catch(function(err){
.catch(function(err) {
done(err);
});
});

View File

@ -1,3 +1,9 @@
// Copyright IBM Corp. 2014,2016. All Rights Reserved.
// Node module: loopback-datasource-juggler
// This file is licensed under the MIT License.
// License text available at https://opensource.org/licenses/MIT
'use strict';
var should = require('./init.js');
describe('events', function() {
@ -6,9 +12,9 @@ describe('events', function() {
this.db = getSchema();
this.TestModel = this.db.define('TestModel');
this.db.automigrate(function(err) {
if(err) return done(err);
if (err) return done(err);
test.TestModel.create(function(err, inst) {
if(err) return done(err);
if (err) return done(err);
test.inst = inst;
done();
});
@ -18,7 +24,7 @@ describe('events', function() {
listener.apply(this, arguments);
done();
});
}
};
});
describe('changed', function() {

View File

@ -0,0 +1,8 @@
// Copyright IBM Corp. 2013,2016. All Rights Reserved.
// Node module: loopback-datasource-juggler
// This file is licensed under the MIT License.
// License text available at https://opensource.org/licenses/MIT
'use strict';
throw new Error('expected test error');

View File

@ -0,0 +1,5 @@
{
"name": "loopback-connector-throwing",
"version": "1.0.0",
"description": "A dummy connector that throws at initialization time."
}

View File

@ -1,3 +1,9 @@
// Copyright IBM Corp. 2014,2016. All Rights Reserved.
// Node module: loopback-datasource-juggler
// This file is licensed under the MIT License.
// License text available at https://opensource.org/licenses/MIT
'use strict';
/*global describe,it*/
/*jshint expr:true */
@ -6,39 +12,37 @@ require('should');
var GeoPoint = require('../lib/geo').GeoPoint;
var DELTA = 0.0000001;
describe('GeoPoint', function () {
describe('GeoPoint', function() {
describe('constructor', function() {
it('should support a valid array', function () {
it('should support a valid array', function() {
var point = new GeoPoint([-34, 150]);
point.lat.should.equal(-34);
point.lng.should.equal(150);
});
it('should support a valid object', function () {
var point = new GeoPoint({ lat: -34, lng: 150 });
it('should support a valid object', function() {
var point = new GeoPoint({lat: -34, lng: 150});
point.lat.should.equal(-34);
point.lng.should.equal(150);
});
it('should support valid string geo coordinates', function () {
it('should support valid string geo coordinates', function() {
var point = new GeoPoint('-34,150');
point.lat.should.equal(-34);
point.lng.should.equal(150);
});
it('should support coordinates as inline parameters', function () {
it('should support coordinates as inline parameters', function() {
var point = new GeoPoint(-34, 150);
point.lat.should.equal(-34);
point.lng.should.equal(150);
});
it('should reject invalid parameters', function () {
it('should reject invalid parameters', function() {
/*jshint -W024 */
var fn = function() {
new GeoPoint('150,-34');
@ -58,7 +62,7 @@ describe('GeoPoint', function () {
fn = function() {
new GeoPoint({
lat: 150,
lng: null
lng: null,
});
};
fn.should.throw();
@ -73,33 +77,26 @@ describe('GeoPoint', function () {
};
fn.should.throw();
});
});
describe('toString()', function() {
it('should return a string in the form "lat,lng"', function() {
var point = new GeoPoint({ lat: -34, lng: 150 });
var point = new GeoPoint({lat: -34, lng: 150});
point.toString().should.equal('-34,150');
});
});
describe('distance calculation between two points', function () {
var here = new GeoPoint({ lat: 40.77492964101182, lng: -73.90950187151662 });
var there = new GeoPoint({ lat: 40.7753227, lng: -73.909217 });
it('should return value in miles by default', function () {
describe('distance calculation between two points', function() {
var here = new GeoPoint({lat: 40.77492964101182, lng: -73.90950187151662});
var there = new GeoPoint({lat: 40.7753227, lng: -73.909217});
it('should return value in miles by default', function() {
var distance = GeoPoint.distanceBetween(here, there);
distance.should.be.a.Number;
distance.should.be.approximately(0.03097916611592679, DELTA);
});
it('should return value using specified unit', function () {
it('should return value using specified unit', function() {
/* Supported units:
* - `radians`
* - `kilometers`
@ -109,31 +106,29 @@ describe('GeoPoint', function () {
* - `degrees`
*/
var distance = here.distanceTo(there, { type: 'radians'});
var distance = here.distanceTo(there, {type: 'radians'});
distance.should.be.a.Number;
distance.should.be.approximately(0.000007825491914348416, DELTA);
distance = here.distanceTo(there, { type: 'kilometers'});
distance = here.distanceTo(there, {type: 'kilometers'});
distance.should.be.a.Number;
distance.should.be.approximately(0.04985613511367009, DELTA);
distance = here.distanceTo(there, { type: 'meters'});
distance = here.distanceTo(there, {type: 'meters'});
distance.should.be.a.Number;
distance.should.be.approximately(49.856135113670085, DELTA);
distance = here.distanceTo(there, { type: 'miles'});
distance = here.distanceTo(there, {type: 'miles'});
distance.should.be.a.Number;
distance.should.be.approximately(0.03097916611592679, DELTA);
distance = here.distanceTo(there, { type: 'feet'});
distance = here.distanceTo(there, {type: 'feet'});
distance.should.be.a.Number;
distance.should.be.approximately(163.56999709209347, DELTA);
distance = here.distanceTo(there, { type: 'degrees'});
distance = here.distanceTo(there, {type: 'degrees'});
distance.should.be.a.Number;
distance.should.be.approximately(0.0004483676593058972, DELTA);
});
});
});

19
test/helpers/bdd-if.js Normal file
View File

@ -0,0 +1,19 @@
// Copyright IBM Corp. 2016. All Rights Reserved.
// Node module: loopback-datasource-juggler
// This file is licensed under the MIT License.
// License text available at https://opensource.org/licenses/MIT
'use strict';
exports.describeIf = function describeIf(cond, name, fn) {
if (cond)
describe(name, fn);
else
describe.skip(name, fn);
};
exports.itIf = function itIf(cond, name, fn) {
if (cond)
it(name, fn);
else
it.skip(name, fn);
};

View File

@ -0,0 +1,72 @@
// Copyright IBM Corp. 2016. All Rights Reserved.
// Node module: loopback-datasource-juggler
// This file is licensed under the MIT License.
// License text available at https://opensource.org/licenses/MIT
'use strict';
var traverse = require('traverse');
exports.ContextRecorder = ContextRecorder;
exports.deepCloneToObject = deepCloneToObject;
exports.aCtxForModel = aCtxForModel;
function ContextRecorder(initialValue) {
if (!(this instanceof ContextRecorder)) {
return new ContextRecorder(initialValue);
}
this.records = initialValue;
};
ContextRecorder.prototype.recordAndNext = function(transformFm) {
var self = this;
return function(context, next) {
if (typeof transformFm === 'function') {
transformFm(context);
}
context = deepCloneToObject(context);
context.hookState.test = true;
if (typeof self.records === 'string') {
self.records = context;
return next();
}
if (!Array.isArray(self.records)) {
self.records = [self.records];
}
self.records.push(context);
next();
};
};
function deepCloneToObject(obj) {
return traverse(obj).map(function(x) {
if (x === undefined) {
// RDBMSs return null
return null;
}
if (x && x.toObject)
return x.toObject(true);
if (x && typeof x === 'function' && x.modelName)
return '[ModelCtor ' + x.modelName + ']';
});
}
function aCtxForModel(TestModel, ctx) {
ctx.Model = TestModel;
if (!ctx.hookState) {
ctx.hookState = {};
}
if (!('test' in ctx.hookState)) {
ctx.hookState.test = true;
}
if (!ctx.options) {
ctx.options = {};
}
return deepCloneToObject(ctx);
}

View File

@ -0,0 +1,35 @@
// Copyright IBM Corp. 2016. All Rights Reserved.
// Node module: loopback-datasource-juggler
// This file is licensed under the MIT License.
// License text available at https://opensource.org/licenses/MIT
'use strict';
module.exports = HookMonitor;
function HookMonitor(opts) {
if (!(this instanceof HookMonitor)) {
return new HookMonitor();
}
this.options = opts || {};
this.names = [];
};
HookMonitor.prototype.install = function(ObservedModel, hookNames) {
var monitor = this;
this.names = [];
ObservedModel._notify = ObservedModel.notifyObserversOf;
ObservedModel.notifyObserversOf = function(operation, context, callback) {
if (!Array.isArray(hookNames) || hookNames.indexOf(operation) !== -1) {
var item = monitor.options.includeModelName ?
ObservedModel.modelName + ':' + operation :
operation;
monitor.names.push(item);
}
this._notify.apply(this, arguments);
};
};
HookMonitor.prototype.resetNames = function() {
this.names = [];
};

View File

@ -0,0 +1,20 @@
// Copyright IBM Corp. 2016. All Rights Reserved.
// Node module: loopback-datasource-juggler
// This file is licensed under the MIT License.
// License text available at https://opensource.org/licenses/MIT
'use strict';
var lastId = 0;
exports.next = function() {
lastId++;
return exports.last();
};
exports.last = function() {
return '' + lastId;
};
exports.reset = function() {
lastId = 0;
};

View File

@ -1,3 +1,9 @@
// Copyright IBM Corp. 2013,2016. All Rights Reserved.
// Node module: loopback-datasource-juggler
// This file is licensed under the MIT License.
// License text available at https://opensource.org/licenses/MIT
'use strict';
// This test written in mocha+should.js
var should = require('./init.js');
@ -8,160 +14,156 @@ var j = require('../'),
db, User;
describe('hooks', function () {
before(function (done) {
describe('hooks', function() {
before(function(done) {
db = getSchema();
User = db.define('User', {
email: {type: String, index: true},
name: String,
password: String,
state: String
state: String,
});
db.automigrate('User', done);
});
describe('initialize', function () {
afterEach(function () {
describe('initialize', function() {
afterEach(function() {
User.afterInitialize = null;
});
it('should be triggered on new', function (done) {
User.afterInitialize = function () {
it('should be triggered on new', function(done) {
User.afterInitialize = function() {
done();
};
new User;
});
it('should be triggered on create', function (done) {
it('should be triggered on create', function(done) {
var user;
User.afterInitialize = function () {
User.afterInitialize = function() {
if (this.name === 'Nickolay') {
this.name += ' Rozental';
}
};
User.create({name: 'Nickolay'}, function (err, u) {
User.create({name: 'Nickolay'}, function(err, u) {
u.id.should.be.ok;
u.name.should.equal('Nickolay Rozental');
done();
});
});
});
describe('create', function () {
describe('create', function() {
afterEach(removeHooks('Create'));
it('should be triggered on create', function (done) {
it('should be triggered on create', function(done) {
addHooks('Create', done);
User.create();
});
it('should not be triggered on new', function () {
User.beforeCreate = function (next) {
it('should not be triggered on new', function() {
User.beforeCreate = function(next) {
should.fail('This should not be called');
next();
};
var u = new User;
});
it('should be triggered on new+save', function (done) {
it('should be triggered on new+save', function(done) {
addHooks('Create', done);
(new User).save();
});
it('afterCreate should not be triggered on failed create', function (done) {
it('afterCreate should not be triggered on failed create', function(done) {
var old = User.dataSource.connector.create;
User.dataSource.connector.create = function (modelName, id, cb) {
User.dataSource.connector.create = function(modelName, id, cb) {
cb(new Error('error'));
}
};
User.afterCreate = function () {
User.afterCreate = function() {
throw new Error('shouldn\'t be called');
};
User.create(function (err, user) {
User.create(function(err, user) {
User.dataSource.connector.create = old;
done();
});
});
it('afterCreate should not be triggered on failed beforeCreate', function (done) {
User.beforeCreate = function (next, data) {
it('afterCreate should not be triggered on failed beforeCreate', function(done) {
User.beforeCreate = function(next, data) {
// Skip next()
next(new Error('fail in beforeCreate'));
};
var old = User.dataSource.connector.create;
User.dataSource.connector.create = function (modelName, id, cb) {
throw new Error('shouldn\'t be called');
}
User.afterCreate = function () {
User.dataSource.connector.create = function(modelName, id, cb) {
throw new Error('shouldn\'t be called');
};
User.create(function (err, user) {
User.afterCreate = function() {
throw new Error('shouldn\'t be called');
};
User.create(function(err, user) {
User.dataSource.connector.create = old;
done();
});
});
});
describe('save', function () {
describe('save', function() {
afterEach(removeHooks('Save'));
it('should be triggered on create', function (done) {
it('should be triggered on create', function(done) {
addHooks('Save', done);
User.create();
});
it('should be triggered on new+save', function (done) {
it('should be triggered on new+save', function(done) {
addHooks('Save', done);
(new User).save();
});
it('should be triggered on updateAttributes', function (done) {
User.create(function (err, user) {
it('should be triggered on updateAttributes', function(done) {
User.create(function(err, user) {
addHooks('Save', done);
user.updateAttributes({name: 'Anatoliy'});
});
});
it('should be triggered on save', function (done) {
User.create(function (err, user) {
it('should be triggered on save', function(done) {
User.create(function(err, user) {
addHooks('Save', done);
user.name = 'Hamburger';
user.save();
});
});
it('should save full object', function (done) {
User.create(function (err, user) {
User.beforeSave = function (next, data) {
it('should save full object', function(done) {
User.create(function(err, user) {
User.beforeSave = function(next, data) {
data.should.have.keys('id', 'name', 'email',
'password', 'state')
'password', 'state');
done();
};
user.save();
});
});
it('should save actual modifications to database', function (done) {
User.beforeSave = function (next, data) {
it('should save actual modifications to database', function(done) {
User.beforeSave = function(next, data) {
data.password = 'hash';
next();
};
User.destroyAll(function () {
User.destroyAll(function() {
User.create({
email: 'james.bond@example.com',
password: '53cr3t'
}, function () {
password: '53cr3t',
}, function() {
User.findOne({
where: {email: 'james.bond@example.com'}
}, function (err, jb) {
where: {email: 'james.bond@example.com'},
}, function(err, jb) {
jb.password.should.equal('hash');
done();
});
@ -169,22 +171,22 @@ describe('hooks', function () {
});
});
it('should save actual modifications on updateAttributes', function (done) {
User.beforeSave = function (next, data) {
it('should save actual modifications on updateAttributes', function(done) {
User.beforeSave = function(next, data) {
data.password = 'hash';
next();
};
User.destroyAll(function () {
User.destroyAll(function() {
User.create({
email: 'james.bond@example.com'
}, function (err, u) {
u.updateAttribute('password', 'new password', function (e, u) {
email: 'james.bond@example.com',
}, function(err, u) {
u.updateAttribute('password', 'new password', function(e, u) {
should.not.exist(e);
should.exist(u);
u.password.should.equal('hash');
User.findOne({
where: {email: 'james.bond@example.com'}
}, function (err, jb) {
where: {email: 'james.bond@example.com'},
}, function(err, jb) {
jb.password.should.equal('hash');
done();
});
@ -193,9 +195,9 @@ describe('hooks', function () {
});
});
it('beforeSave should be able to skip next', function (done) {
User.create(function (err, user) {
User.beforeSave = function (next, data) {
it('beforeSave should be able to skip next', function(done) {
User.create(function(err, user) {
User.beforeSave = function(next, data) {
next(null, 'XYZ');
};
user.save(function(err, result) {
@ -204,46 +206,45 @@ describe('hooks', function () {
});
});
});
});
describe('update', function () {
describe('update', function() {
afterEach(removeHooks('Update'));
it('should not be triggered on create', function () {
User.beforeUpdate = function (next) {
it('should not be triggered on create', function() {
User.beforeUpdate = function(next) {
should.fail('This should not be called');
next();
};
User.create();
});
it('should not be triggered on new+save', function () {
User.beforeUpdate = function (next) {
it('should not be triggered on new+save', function() {
User.beforeUpdate = function(next) {
should.fail('This should not be called');
next();
};
(new User).save();
});
it('should be triggered on updateAttributes', function (done) {
User.create(function (err, user) {
it('should be triggered on updateAttributes', function(done) {
User.create(function(err, user) {
addHooks('Update', done);
user.updateAttributes({name: 'Anatoliy'});
});
});
it('should be triggered on save', function (done) {
User.create(function (err, user) {
it('should be triggered on save', function(done) {
User.create(function(err, user) {
addHooks('Update', done);
user.name = 'Hamburger';
user.save();
});
});
it('should update limited set of fields', function (done) {
User.create(function (err, user) {
User.beforeUpdate = function (next, data) {
it('should update limited set of fields', function(done) {
User.create(function(err, user) {
User.beforeUpdate = function(next, data) {
data.should.have.keys('name', 'email');
done();
};
@ -251,119 +252,117 @@ describe('hooks', function () {
});
});
it('should not trigger after-hook on failed save', function (done) {
User.afterUpdate = function () {
it('should not trigger after-hook on failed save', function(done) {
User.afterUpdate = function() {
should.fail('afterUpdate shouldn\'t be called');
};
User.create(function (err, user) {
User.create(function(err, user) {
var save = User.dataSource.connector.save;
User.dataSource.connector.save = function (modelName, id, cb) {
User.dataSource.connector.save = function(modelName, id, cb) {
User.dataSource.connector.save = save;
cb(new Error('Error'));
}
};
user.save(function (err) {
user.save(function(err) {
done();
});
});
});
});
describe('destroy', function () {
describe('destroy', function() {
afterEach(removeHooks('Destroy'));
it('should be triggered on destroy', function (done) {
it('should be triggered on destroy', function(done) {
var hook = 'not called';
User.beforeDestroy = function (next) {
User.beforeDestroy = function(next) {
hook = 'called';
next();
};
User.afterDestroy = function (next) {
User.afterDestroy = function(next) {
hook.should.eql('called');
next();
};
User.create(function (err, user) {
User.create(function(err, user) {
user.destroy(done);
});
});
it('should not trigger after-hook on failed destroy', function (done) {
it('should not trigger after-hook on failed destroy', function(done) {
var destroy = User.dataSource.connector.destroy;
User.dataSource.connector.destroy = function (modelName, id, cb) {
User.dataSource.connector.destroy = function(modelName, id, cb) {
cb(new Error('error'));
}
User.afterDestroy = function () {
should.fail('afterDestroy shouldn\'t be called')
};
User.create(function (err, user) {
user.destroy(function (err) {
User.afterDestroy = function() {
should.fail('afterDestroy shouldn\'t be called');
};
User.create(function(err, user) {
user.destroy(function(err) {
User.dataSource.connector.destroy = destroy;
done();
});
});
});
});
describe('lifecycle', function () {
describe('lifecycle', function() {
var life = [], user;
before(function (done) {
User.beforeSave = function (d) {
before(function(done) {
User.beforeSave = function(d) {
life.push('beforeSave');
d();
};
User.beforeCreate = function (d) {
User.beforeCreate = function(d) {
life.push('beforeCreate');
d();
};
User.beforeUpdate = function (d) {
User.beforeUpdate = function(d) {
life.push('beforeUpdate');
d();
};
User.beforeDestroy = function (d) {
User.beforeDestroy = function(d) {
life.push('beforeDestroy');
d();
};
User.beforeValidate = function (d) {
User.beforeValidate = function(d) {
life.push('beforeValidate');
d();
};
User.afterInitialize = function () {
User.afterInitialize = function() {
life.push('afterInitialize');
};
User.afterSave = function (d) {
User.afterSave = function(d) {
life.push('afterSave');
d();
};
User.afterCreate = function (d) {
User.afterCreate = function(d) {
life.push('afterCreate');
d();
};
User.afterUpdate = function (d) {
User.afterUpdate = function(d) {
life.push('afterUpdate');
d();
};
User.afterDestroy = function (d) {
User.afterDestroy = function(d) {
life.push('afterDestroy');
d();
};
User.afterValidate = function (d) {
User.afterValidate = function(d) {
life.push('afterValidate');
d();
};
User.create(function (e, u) {
User.create(function(e, u) {
user = u;
life = [];
done();
});
});
beforeEach(function () {
beforeEach(function() {
life = [];
});
it('should describe create sequence', function (done) {
User.create(function () {
it('should describe create sequence', function(done) {
User.create(function() {
life.should.eql([
'afterInitialize',
'beforeValidate',
@ -371,15 +370,15 @@ describe('hooks', function () {
'beforeCreate',
'beforeSave',
'afterSave',
'afterCreate'
'afterCreate',
]);
done();
});
});
it('should describe new+save sequence', function (done) {
it('should describe new+save sequence', function(done) {
var u = new User;
u.save(function () {
u.save(function() {
life.should.eql([
'afterInitialize',
'beforeValidate',
@ -387,14 +386,14 @@ describe('hooks', function () {
'beforeCreate',
'beforeSave',
'afterSave',
'afterCreate'
'afterCreate',
]);
done();
});
});
it('should describe updateAttributes sequence', function (done) {
user.updateAttributes({name: 'Antony'}, function () {
it('should describe updateAttributes sequence', function(done) {
user.updateAttributes({name: 'Antony'}, function() {
life.should.eql([
'beforeValidate',
'afterValidate',
@ -407,43 +406,40 @@ describe('hooks', function () {
});
});
it('should describe isValid sequence', function (done) {
it('should describe isValid sequence', function(done) {
should.not.exist(
user.constructor._validations,
'Expected user to have no validations, but she have');
user.isValid(function (valid) {
user.isValid(function(valid) {
valid.should.be.true;
life.should.eql([
'beforeValidate',
'afterValidate'
'afterValidate',
]);
done();
});
});
it('should describe destroy sequence', function (done) {
user.destroy(function () {
it('should describe destroy sequence', function(done) {
user.destroy(function() {
life.should.eql([
'beforeDestroy',
'afterDestroy'
'afterDestroy',
]);
done();
});
});
});
});
function addHooks(name, done) {
var called = false, random = String(Math.floor(Math.random() * 1000));
User['before' + name] = function (next, data) {
User['before' + name] = function(next, data) {
called = true;
data.email = random;
next();
};
User['after' + name] = function (next) {
User['after' + name] = function(next) {
(new Boolean(called)).should.equal(true);
this.should.have.property('email', random);
done();
@ -451,7 +447,7 @@ function addHooks(name, done) {
}
function removeHooks(name) {
return function () {
return function() {
User['after' + name] = null;
User['before' + name] = null;
};

View File

@ -1,3 +1,9 @@
// Copyright IBM Corp. 2013,2015. All Rights Reserved.
// Node module: loopback-datasource-juggler
// This file is licensed under the MIT License.
// License text available at https://opensource.org/licenses/MIT
'use strict';
// This test written in mocha+should.js
var should = require('./init.js');
var async = require('async');
@ -7,14 +13,13 @@ var DataSource = require('../').DataSource;
var db, User, Profile, AccessToken, Post, Passport, City, Street, Building, Assembly, Part;
describe('include', function () {
describe('include', function() {
before(setup);
it('should fetch belongsTo relation', function (done) {
Passport.find({include: 'owner'}, function (err, passports) {
it('should fetch belongsTo relation', function(done) {
Passport.find({include: 'owner'}, function(err, passports) {
passports.length.should.be.ok;
passports.forEach(function (p) {
passports.forEach(function(p) {
p.__cachedRelations.should.have.property('owner');
// The relation should be promoted as the 'owner' property
@ -34,19 +39,19 @@ describe('include', function () {
});
});
it('should fetch hasMany relation', function (done) {
User.find({include: 'posts'}, function (err, users) {
it('should fetch hasMany relation', function(done) {
User.find({include: 'posts'}, function(err, users) {
should.not.exist(err);
should.exist(users);
users.length.should.be.ok;
users.forEach(function (u) {
users.forEach(function(u) {
// The relation should be promoted as the 'owner' property
u.should.have.property('posts');
// The __cachedRelations should be removed from json output
u.toJSON().should.not.have.property('__cachedRelations');
u.__cachedRelations.should.have.property('posts');
u.__cachedRelations.posts.forEach(function (p) {
u.__cachedRelations.posts.forEach(function(p) {
p.userId.should.eql(u.id);
});
});
@ -54,13 +59,12 @@ describe('include', function () {
});
});
it('should fetch Passport - Owner - Posts', function (done) {
Passport.find({include: {owner: 'posts'}}, function (err, passports) {
it('should fetch Passport - Owner - Posts', function(done) {
Passport.find({include: {owner: 'posts'}}, function(err, passports) {
should.not.exist(err);
should.exist(passports);
passports.length.should.be.ok;
passports.forEach(function (p) {
passports.forEach(function(p) {
p.__cachedRelations.should.have.property('owner');
// The relation should be promoted as the 'owner' property
@ -77,7 +81,7 @@ describe('include', function () {
user.__cachedRelations.should.have.property('posts');
user.should.have.property('posts');
user.toJSON().should.have.property('posts').and.be.an.Array;
user.__cachedRelations.posts.forEach(function (pp) {
user.__cachedRelations.posts.forEach(function(pp) {
pp.userId.should.eql(user.id);
});
}
@ -86,8 +90,8 @@ describe('include', function () {
});
});
it('should fetch Passport - Owner - empty Posts', function (done) {
Passport.findOne({where: {number: '4'}, include: {owner: 'posts'}}, function (err, passport) {
it('should fetch Passport - Owner - empty Posts', function(done) {
Passport.findOne({where: {number: '4'}, include: {owner: 'posts'}}, function(err, passport) {
should.not.exist(err);
should.exist(passport);
passport.__cachedRelations.should.have.property('owner');
@ -102,13 +106,14 @@ describe('include', function () {
user.id.should.eql(passport.ownerId);
user.__cachedRelations.should.have.property('posts');
user.should.have.property('posts');
user.toJSON().should.have.property('posts').and.be.an.Array.with.lengthOf(0);
user.toJSON().should.have.property('posts').and.be.an.Array().with
.length(0);
done();
});
});
it('should fetch Passport - Owner - Posts - alternate syntax', function (done) {
Passport.find({include: {owner: {relation: 'posts'}}}, function (err, passports) {
it('should fetch Passport - Owner - Posts - alternate syntax', function(done) {
Passport.find({include: {owner: {relation: 'posts'}}}, function(err, passports) {
should.not.exist(err);
should.exist(passports);
passports.length.should.be.ok;
@ -118,14 +123,14 @@ describe('include', function () {
});
});
it('should fetch Passports - User - Posts - User', function (done) {
it('should fetch Passports - User - Posts - User', function(done) {
Passport.find({
include: {owner: {posts: 'author'}}
}, function (err, passports) {
include: {owner: {posts: 'author'}},
}, function(err, passports) {
should.not.exist(err);
should.exist(passports);
passports.length.should.be.ok;
passports.forEach(function (p) {
passports.forEach(function(p) {
p.__cachedRelations.should.have.property('owner');
var user = p.__cachedRelations.owner;
if (!p.ownerId) {
@ -134,7 +139,7 @@ describe('include', function () {
should.exist(user);
user.id.should.eql(p.ownerId);
user.__cachedRelations.should.have.property('posts');
user.__cachedRelations.posts.forEach(function (pp) {
user.__cachedRelations.posts.forEach(function(pp) {
pp.should.have.property('id');
pp.userId.should.eql(user.id);
pp.should.have.property('author');
@ -148,13 +153,13 @@ describe('include', function () {
});
});
it('should fetch Passports with include scope on Posts', function (done) {
it('should fetch Passports with include scope on Posts', function(done) {
Passport.find({
include: {owner: {relation: 'posts', scope:{
include: {owner: {relation: 'posts', scope: {
fields: ['title'], include: ['author'],
order: 'title DESC'
}}}
}, function (err, passports) {
order: 'title DESC',
}}},
}, function(err, passports) {
should.not.exist(err);
should.exist(passports);
passports.length.should.equal(4);
@ -190,11 +195,11 @@ describe('include', function () {
relation: 'posts', scope: {
fields: ['title'], include: ['author'],
order: 'title DESC',
limit: 2
}
}
limit: 2,
},
},
},
limit: 1
limit: 1,
}, function(err, passports) {
if (err) return done(err);
passports.length.should.equal(1);
@ -203,29 +208,32 @@ describe('include', function () {
});
});
it('should fetch Users with include scope on Posts - belongsTo', function (done) {
Post.find({
include: { relation: 'author', scope:{ fields: ['name'] }}
}, function (err, posts) {
should.not.exist(err);
should.exist(posts);
posts.length.should.equal(5);
it('should fetch Users with include scope on Posts - belongsTo', function(done) {
Post.find({
include: {relation: 'author', scope: {fields: ['name']}},
}, function(err, posts) {
should.not.exist(err);
should.exist(posts);
posts.length.should.equal(5);
var author = posts[0].author();
author.name.should.equal('User A');
author.should.have.property('id');
author.should.have.property('age', undefined);
var author = posts[0].author();
author.name.should.equal('User A');
author.should.have.property('id');
author.should.have.property('age', undefined);
done();
});
done();
});
});
it('should fetch Users with include scope on Posts - hasMany', function (done) {
it('should fetch Users with include scope on Posts - hasMany', function(done) {
User.find({
include: {relation: 'posts', scope:{
order: 'title DESC'
}}
}, function (err, users) {
include: {
relation: 'posts',
scope: {
order: 'title DESC',
},
},
}, function(err, users) {
should.not.exist(err);
should.exist(users);
users.length.should.equal(5);
@ -250,12 +258,15 @@ describe('include', function () {
});
});
it('should fetch Users with include scope on Passports - hasMany', function (done) {
it('should fetch Users with include scope on Passports - hasMany', function(done) {
User.find({
include: {relation: 'passports', scope:{
where: { number: '2' }
}}
}, function (err, users) {
include: {
relation: 'passports',
scope: {
where: {number: '2'},
},
},
}, function(err, users) {
should.not.exist(err);
should.exist(users);
users.length.should.equal(5);
@ -271,12 +282,12 @@ describe('include', function () {
});
});
it('should fetch User - Posts AND Passports', function (done) {
User.find({include: ['posts', 'passports']}, function (err, users) {
it('should fetch User - Posts AND Passports', function(done) {
User.find({include: ['posts', 'passports']}, function(err, users) {
should.not.exist(err);
should.exist(users);
users.length.should.be.ok;
users.forEach(function (user) {
users.forEach(function(user) {
// The relation should be promoted as the 'owner' property
user.should.have.property('posts');
user.should.have.property('passports');
@ -292,10 +303,10 @@ describe('include', function () {
user.__cachedRelations.should.have.property('posts');
user.__cachedRelations.should.have.property('passports');
user.__cachedRelations.posts.forEach(function (p) {
user.__cachedRelations.posts.forEach(function(p) {
p.userId.should.eql(user.id);
});
user.__cachedRelations.passports.forEach(function (pp) {
user.__cachedRelations.passports.forEach(function(pp) {
pp.ownerId.should.eql(user.id);
});
});
@ -307,9 +318,9 @@ describe('include', function () {
function(done) {
User.find({include: [
{relation: 'posts', scope: {
where: {title: 'Post A'}
where: {title: 'Post A'},
}},
'passports'
'passports',
]}, function(err, users) {
should.not.exist(err);
should.exist(users);
@ -342,12 +353,12 @@ describe('include', function () {
});
});
it('should not fetch User - AccessTokens', function (done) {
User.find({include: ['accesstokens']}, function (err, users) {
it('should not fetch User - AccessTokens', function(done) {
User.find({include: ['accesstokens']}, function(err, users) {
should.not.exist(err);
should.exist(users);
users.length.should.be.ok;
users.forEach(function (user) {
users.forEach(function(user) {
var userObj = user.toJSON();
userObj.should.not.have.property('accesstokens');
});
@ -355,25 +366,23 @@ describe('include', function () {
});
});
it('should support hasAndBelongsToMany', function (done) {
Assembly.create({name: 'car'}, function (err, assembly) {
Part.create({partNumber: 'engine'}, function (err, part) {
assembly.parts.add(part, function (err, data) {
assembly.parts(function (err, parts) {
it('should support hasAndBelongsToMany', function(done) {
Assembly.create({name: 'car'}, function(err, assembly) {
Part.create({partNumber: 'engine'}, function(err, part) {
assembly.parts.add(part, function(err, data) {
assembly.parts(function(err, parts) {
should.not.exist(err);
should.exists(parts);
parts.length.should.equal(1);
parts[0].partNumber.should.equal('engine');
// Create a part
assembly.parts.create({partNumber: 'door'}, function (err, part4) {
Assembly.find({include: 'parts'}, function (err, assemblies) {
assembly.parts.create({partNumber: 'door'}, function(err, part4) {
Assembly.find({include: 'parts'}, function(err, assemblies) {
assemblies.length.should.equal(1);
assemblies[0].parts().length.should.equal(2);
done();
});
});
});
});
@ -381,13 +390,13 @@ describe('include', function () {
});
});
it('should fetch User - Profile (HasOne)', function (done) {
User.find({include: ['profile']}, function (err, users) {
it('should fetch User - Profile (HasOne)', function(done) {
User.find({include: ['profile']}, function(err, users) {
should.not.exist(err);
should.exist(users);
users.length.should.be.ok;
var usersWithProfile = 0;
users.forEach(function (user) {
users.forEach(function(user) {
// The relation should be promoted as the 'owner' property
user.should.have.property('profile');
var userObj = user.toJSON();
@ -395,8 +404,7 @@ describe('include', function () {
if (profile) {
profile.should.be.an.instanceOf(Profile);
usersWithProfile++;
}
else {
} else {
(profile === null).should.be.true;
}
// The __cachedRelations should be removed from json output
@ -412,13 +420,12 @@ describe('include', function () {
});
});
// Not implemented correctly, see: loopback-datasource-juggler/issues/166
// fixed by DB optimization
it('should support include scope on hasAndBelongsToMany', function (done) {
Assembly.find({include: { relation: 'parts', scope: {
where: { partNumber: 'engine' }
}}}, function (err, assemblies) {
it('should support include scope on hasAndBelongsToMany', function(done) {
Assembly.find({include: {relation: 'parts', scope: {
where: {partNumber: 'engine'},
}}}, function(err, assemblies) {
assemblies.length.should.equal(1);
var parts = assemblies[0].parts();
parts.should.have.length(1);
@ -427,7 +434,29 @@ describe('include', function () {
});
});
describe('performance', function () {
it('should save related items separately', function(done) {
User.find({
include: 'posts',
})
.then(function(users) {
var posts = users[0].posts();
posts.should.have.length(3);
return users[0].save();
})
.then(function(updatedUser) {
return User.findById(updatedUser.id, {
include: 'posts',
});
})
.then(function(user) {
var posts = user.posts();
posts.should.have.length(3);
})
.then(done)
.catch(done);
});
describe('performance', function() {
var all;
beforeEach(function() {
this.called = 0;
@ -441,11 +470,11 @@ describe('include', function () {
afterEach(function() {
db.connector.all = all;
});
it('including belongsTo should make only 2 db calls', function (done) {
it('including belongsTo should make only 2 db calls', function(done) {
var self = this;
Passport.find({include: 'owner'}, function (err, passports) {
Passport.find({include: 'owner'}, function(err, passports) {
passports.length.should.be.ok;
passports.forEach(function (p) {
passports.forEach(function(p) {
p.__cachedRelations.should.have.property('owner');
// The relation should be promoted as the 'owner' property
p.should.have.property('owner');
@ -464,16 +493,16 @@ describe('include', function () {
});
});
it('including hasManyThrough should make only 3 db calls', function (done) {
it('including hasManyThrough should make only 3 db calls', function(done) {
var self = this;
Assembly.create([{name: 'sedan'}, {name: 'hatchback'},
{name: 'SUV'}],
function (err, assemblies) {
function(err, assemblies) {
Part.create([{partNumber: 'engine'}, {partNumber: 'bootspace'},
{partNumber: 'silencer'}],
function (err, parts) {
async.each(parts, function (part, next) {
async.each(assemblies, function (assembly, next) {
function(err, parts) {
async.each(parts, function(part, next) {
async.each(assemblies, function(assembly, next) {
if (assembly.name === 'SUV') {
return next();
}
@ -481,20 +510,20 @@ describe('include', function () {
part.partNumber === 'bootspace') {
return next();
}
assembly.parts.add(part, function (err, data) {
assembly.parts.add(part, function(err, data) {
next();
});
}, next);
}, function (err) {
}, function(err) {
self.called = 0;
Assembly.find({
where: {
name: {
inq: ['sedan', 'hatchback', 'SUV']
}
inq: ['sedan', 'hatchback', 'SUV'],
},
},
include: 'parts'
}, function (err, result) {
include: 'parts',
}, function(err, result) {
should.not.exist(err);
should.exists(result);
result.length.should.equal(3);
@ -517,13 +546,13 @@ describe('include', function () {
});
});
it('including hasMany should make only 2 db calls', function (done) {
it('including hasMany should make only 2 db calls', function(done) {
var self = this;
User.find({include: ['posts', 'passports']}, function (err, users) {
User.find({include: ['posts', 'passports']}, function(err, users) {
should.not.exist(err);
should.exist(users);
users.length.should.be.ok;
users.forEach(function (user) {
users.forEach(function(user) {
// The relation should be promoted as the 'owner' property
user.should.have.property('posts');
user.should.have.property('passports');
@ -539,10 +568,10 @@ describe('include', function () {
user.__cachedRelations.should.have.property('posts');
user.__cachedRelations.should.have.property('passports');
user.__cachedRelations.posts.forEach(function (p) {
user.__cachedRelations.posts.forEach(function(p) {
p.userId.should.eql(user.id);
});
user.__cachedRelations.passports.forEach(function (pp) {
user.__cachedRelations.passports.forEach(function(pp) {
pp.ownerId.should.eql(user.id);
});
});
@ -551,17 +580,16 @@ describe('include', function () {
});
});
it('should not make n+1 db calls in relation syntax',
function (done) {
function(done) {
var self = this;
User.find({include: [{ relation: 'posts', scope: {
where: {title: 'Post A'}
}}, 'passports']}, function (err, users) {
User.find({include: [{relation: 'posts', scope: {
where: {title: 'Post A'},
}}, 'passports']}, function(err, users) {
should.not.exist(err);
should.exist(users);
users.length.should.be.ok;
users.forEach(function (user) {
users.forEach(function(user) {
// The relation should be promoted as the 'owner' property
user.should.have.property('posts');
user.should.have.property('passports');
@ -577,11 +605,11 @@ describe('include', function () {
user.__cachedRelations.should.have.property('posts');
user.__cachedRelations.should.have.property('passports');
user.__cachedRelations.posts.forEach(function (p) {
user.__cachedRelations.posts.forEach(function(p) {
p.userId.should.eql(user.id);
p.title.should.be.equal('Post A');
});
user.__cachedRelations.passports.forEach(function (pp) {
user.__cachedRelations.passports.forEach(function(pp) {
pp.ownerId.should.eql(user.id);
});
});
@ -590,6 +618,29 @@ describe('include', function () {
});
});
});
it('should support disableInclude for hasAndBelongsToMany', function() {
var Patient = db.define('Patient', {name: String});
var Doctor = db.define('Doctor', {name: String});
var DoctorPatient = db.define('DoctorPatient');
Doctor.hasAndBelongsToMany('patients', {
model: 'Patient',
options: {disableInclude: true},
});
var doctor;
return db.automigrate(['Patient', 'Doctor', 'DoctorPatient']).then(function() {
return Doctor.create({name: 'Who'});
}).then(function(inst) {
doctor = inst;
return doctor.patients.create({name: 'Lazarus'});
}).then(function() {
return Doctor.find({include: ['patients']});
}).then(function(list) {
list.should.have.length(1);
list[0].toJSON().should.not.have.property('patients');
});
});
});
function setup(done) {
@ -599,19 +650,19 @@ function setup(done) {
Building = db.define('Building');
User = db.define('User', {
name: String,
age: Number
age: Number,
});
Profile = db.define('Profile', {
profileName: String
profileName: String,
});
AccessToken = db.define('AccessToken', {
token: String
token: String,
});
Passport = db.define('Passport', {
number: String
number: String,
});
Post = db.define('Post', {
title: String
title: String,
});
Passport.belongsTo('owner', {model: User});
@ -619,24 +670,24 @@ function setup(done) {
User.hasMany('posts', {foreignKey: 'userId'});
User.hasMany('accesstokens', {
foreignKey: 'userId',
options: {disableInclude: true}
options: {disableInclude: true},
});
Profile.belongsTo('user', {model: User});
User.hasOne('profile', {foreignKey: 'userId'});
Post.belongsTo('author', {model: User, foreignKey: 'userId'});
Assembly = db.define('Assembly', {
name: String
name: String,
});
Part = db.define('Part', {
partNumber: String
partNumber: String,
});
Assembly.hasAndBelongsToMany(Part);
Part.hasAndBelongsToMany(Assembly);
db.automigrate(function () {
db.automigrate(function() {
var createdUsers = [];
var createdPassports = [];
var createdProfiles = [];
@ -650,9 +701,9 @@ function setup(done) {
{name: 'User B', age: 22},
{name: 'User C', age: 23},
{name: 'User D', age: 24},
{name: 'User E', age: 25}
{name: 'User E', age: 25},
],
function (items) {
function(items) {
createdUsers = items;
createPassports();
createAccessTokens();
@ -665,9 +716,9 @@ function setup(done) {
AccessToken,
[
{token: '1', userId: createdUsers[0].id},
{token: '2', userId: createdUsers[1].id}
{token: '2', userId: createdUsers[1].id},
],
function (items) {}
function(items) {}
);
}
@ -680,7 +731,7 @@ function setup(done) {
{number: '3'},
{number: '4', ownerId: createdUsers[2].id},
],
function (items) {
function(items) {
createdPassports = items;
createPosts();
}
@ -693,10 +744,10 @@ function setup(done) {
[
{profileName: 'Profile A', userId: createdUsers[0].id},
{profileName: 'Profile B', userId: createdUsers[1].id},
{profileName: 'Profile Z'}
{profileName: 'Profile Z'},
],
function (items) {
createdProfiles = items
function(items) {
createdProfiles = items;
done();
}
);
@ -710,21 +761,20 @@ function setup(done) {
{title: 'Post B', userId: createdUsers[0].id},
{title: 'Post C', userId: createdUsers[0].id},
{title: 'Post D', userId: createdUsers[1].id},
{title: 'Post E'}
{title: 'Post E'},
],
function (items) {
function(items) {
createdPosts = items;
createProfiles();
}
);
}
});
}
function clearAndCreate(model, data, callback) {
var createdItems = [];
model.destroyAll(function () {
model.destroyAll(function() {
nextItem(null, null);
});
@ -750,35 +800,35 @@ describe('Model instance with included relation .toJSON()', function() {
db = new DataSource({connector: 'memory'});
ChallengerModel = db.createModel('Challenger',
{
name: String
name: String,
},
{
relations: {
gameParticipations: {
type: 'hasMany',
model: 'GameParticipation',
foreignKey: ''
}
}
foreignKey: '',
},
},
}
);
GameParticipationModel = db.createModel('GameParticipation',
{
date: Date
date: Date,
},
{
relations: {
challenger: {
type: 'belongsTo',
model: 'Challenger',
foreignKey: ''
foreignKey: '',
},
results: {
type: 'hasMany',
model: 'Result',
foreignKey: ''
}
}
foreignKey: '',
},
},
}
);
ResultModel = db.createModel('Result', {
@ -788,9 +838,9 @@ describe('Model instance with included relation .toJSON()', function() {
gameParticipation: {
type: 'belongsTo',
model: 'GameParticipation',
foreignKey: ''
}
}
foreignKey: '',
},
},
});
async.waterfall([
@ -800,7 +850,6 @@ describe('Model instance with included relation .toJSON()', function() {
function(err) {
done(err);
});
});
function createChallengers(callback) {
@ -810,21 +859,20 @@ describe('Model instance with included relation .toJSON()', function() {
function createGameParticipations(challengers, callback) {
GameParticipationModel.create([
{challengerId: challengers[0].id, date: Date.now()},
{challengerId: challengers[0].id, date: Date.now()}
{challengerId: challengers[0].id, date: Date.now()},
], callback);
}
function createResults(gameParticipations, callback) {
ResultModel.create([
{gameParticipationId: gameParticipations[0].id, points: 10},
{gameParticipationId: gameParticipations[0].id, points: 20}
{gameParticipationId: gameParticipations[0].id, points: 20},
], callback);
}
it('should recursively serialize objects', function(done) {
var filter = {include: {gameParticipations: 'results'}};
ChallengerModel.find(filter, function(err, challengers) {
var levelOneInclusion = challengers[0].toJSON().gameParticipations[0];
assert(levelOneInclusion.__data === undefined, '.__data of a level 1 inclusion is undefined.');

View File

@ -1,41 +1,49 @@
var assert = require("assert");
var should = require("should");
// Copyright IBM Corp. 2015,2016. All Rights Reserved.
// Node module: loopback-datasource-juggler
// This file is licensed under the MIT License.
// License text available at https://opensource.org/licenses/MIT
'use strict';
var includeUtils = require("../lib/include_utils");
/* eslint-disable camelcase */
describe('include_util', function(){
describe('#buildOneToOneIdentityMapWithOrigKeys', function(){
it('should return an object with keys', function(){
var assert = require('assert');
var should = require('should');
var includeUtils = require('../lib/include_utils');
describe('include_util', function() {
describe('#buildOneToOneIdentityMapWithOrigKeys', function() {
it('should return an object with keys', function() {
var objs = [
{id: 11, letter: "A"},
{id: 22, letter: "B"}
{id: 11, letter: 'A'},
{id: 22, letter: 'B'},
];
var result = includeUtils.buildOneToOneIdentityMapWithOrigKeys(objs, "id");
var result = includeUtils.buildOneToOneIdentityMapWithOrigKeys(objs, 'id');
result.get(11).should.be.ok;
result.get(22).should.be.ok;
});
it('should overwrite keys in case of collision', function(){
var objs = [
{id: 11, letter: "A"},
{id: 22, letter: "B"},
{id: 33, letter: "C"},
{id: 11, letter: "HA!"}
];
it('should overwrite keys in case of collision', function() {
var objs = [
{id: 11, letter: 'A'},
{id: 22, letter: 'B'},
{id: 33, letter: 'C'},
{id: 11, letter: 'HA!'},
];
var result = includeUtils.buildOneToOneIdentityMapWithOrigKeys(objs, "id");
result.getKeys().should.containEql(11);
result.getKeys().should.containEql(22);
result.getKeys().should.containEql(33);
result.get(11)["letter"].should.equal("HA!");
result.get(33)["letter"].should.equal("C");
var result = includeUtils.buildOneToOneIdentityMapWithOrigKeys(objs, 'id');
result.getKeys().should.containEql(11);
result.getKeys().should.containEql(22);
result.getKeys().should.containEql(33);
result.get(11)['letter'].should.equal('HA!');
result.get(33)['letter'].should.equal('C');
});
});
describe('#buildOneToOneIdentityMapWithOrigKeys', function(){
it('should return an object with keys', function(){
describe('#buildOneToOneIdentityMapWithOrigKeys', function() {
it('should return an object with keys', function() {
var objs = [
{id: 11, letter: "A"},
{id: 22, letter: "B"}
{id: 11, letter: 'A'},
{id: 22, letter: 'B'},
];
var result = includeUtils.buildOneToOneIdentityMapWithOrigKeys(objs, 'id');
result.get(11).should.be.ok;
@ -43,36 +51,35 @@ describe('include_util', function(){
result.getKeys().should.have.lengthOf(2); // no additional properties
});
});
describe('#buildOneToManyIdentityMap', function(){
it('should return an object with keys', function(){
var objs = [
{id: 11, letter: "A"},
{id: 22, letter: "B"}
];
var result = includeUtils.buildOneToManyIdentityMapWithOrigKeys(objs, "id");
result.exist(11).should.be.true;
result.exist(22).should.be.true;
});
it('should collect keys in case of collision', function(){
var objs = [
{fk_id: 11, letter: "A"},
{fk_id: 22, letter: "B"},
{fk_id: 33, letter: "C"},
{fk_id: 11, letter: "HA!"}
];
var result = includeUtils.buildOneToManyIdentityMapWithOrigKeys(objs, "fk_id");
result.get(11)[0]["letter"].should.equal("A");
result.get(11)[1]["letter"].should.equal("HA!");
result.get(33)[0]["letter"].should.equal("C");
});
describe('#buildOneToManyIdentityMap', function() {
it('should return an object with keys', function() {
var objs = [
{id: 11, letter: 'A'},
{id: 22, letter: 'B'},
];
var result = includeUtils.buildOneToManyIdentityMapWithOrigKeys(objs, 'id');
result.exist(11).should.be.true;
result.exist(22).should.be.true;
});
it('should collect keys in case of collision', function() {
var objs = [
{fk_id: 11, letter: 'A'},
{fk_id: 22, letter: 'B'},
{fk_id: 33, letter: 'C'},
{fk_id: 11, letter: 'HA!'},
];
var result = includeUtils.buildOneToManyIdentityMapWithOrigKeys(objs, 'fk_id');
result.get(11)[0]['letter'].should.equal('A');
result.get(11)[1]['letter'].should.equal('HA!');
result.get(33)[0]['letter'].should.equal('C');
});
});
});
describe('KVMap', function(){
it('should allow to set and get value with key string', function(){
describe('KVMap', function() {
it('should allow to set and get value with key string', function() {
var map = new includeUtils.KVMap();
map.set('name', 'Alex');
map.set('gender', true);
@ -81,7 +88,7 @@ describe('KVMap', function(){
map.get('gender').should.be.equal(true);
map.get('age').should.be.equal(25);
});
it('should allow to set and get value with arbitrary key type', function(){
it('should allow to set and get value with arbitrary key type', function() {
var map = new includeUtils.KVMap();
map.set('name', 'Alex');
map.set(true, 'male');
@ -92,12 +99,12 @@ describe('KVMap', function(){
map.get(false).should.be.equal(false);
map.get({isTrue: 'yes'}).should.be.equal(25);
});
it('should not allow to get values with [] operator', function(){
it('should not allow to get values with [] operator', function() {
var map = new includeUtils.KVMap();
map.set('name', 'Alex');
(map['name'] === undefined).should.be.equal(true);
});
it('should provide .exist() method for checking if key presented', function(){
it('should provide .exist() method for checking if key presented', function() {
var map = new includeUtils.KVMap();
map.set('one', 1);
map.set(2, 'two');
@ -107,7 +114,7 @@ describe('KVMap', function(){
map.exist(true).should.be.true;
map.exist('two').should.be.false;
});
it('should return array of original keys with .getKeys()', function(){
it('should return array of original keys with .getKeys()', function() {
var map = new includeUtils.KVMap();
map.set('one', 1);
map.set(2, 'two');
@ -117,7 +124,7 @@ describe('KVMap', function(){
keys.should.containEql(2);
keys.should.containEql(true);
});
it('should allow to store and fetch arrays', function(){
it('should allow to store and fetch arrays', function() {
var map = new includeUtils.KVMap();
map.set(1, [1, 2, 3]);
map.set(2, [2, 3, 4]);

View File

@ -1,3 +1,9 @@
// Copyright IBM Corp. 2013,2016. All Rights Reserved.
// Node module: loopback-datasource-juggler
// This file is licensed under the MIT License.
// License text available at https://opensource.org/licenses/MIT
'use strict';
module.exports = require('should');
/*
@ -16,13 +22,13 @@ var ModelBuilder = require('../').ModelBuilder;
var Schema = require('../').Schema;
if (!('getSchema' in global)) {
global.getSchema = function (connector, settings) {
global.getSchema = function(connector, settings) {
return new Schema(connector || 'memory', settings);
};
}
if (!('getModelBuilder' in global)) {
global.getModelBuilder = function () {
global.getModelBuilder = function() {
return new ModelBuilder();
};
}
@ -30,3 +36,7 @@ if (!('getModelBuilder' in global)) {
if (!('Promise' in global)) {
global.Promise = require('bluebird');
}
if (!('connectorCapabilities' in global)) {
global.connectorCapabilities = {};
}

View File

@ -1,3 +1,9 @@
// Copyright IBM Corp. 2013,2016. All Rights Reserved.
// Node module: loopback-datasource-juggler
// This file is licensed under the MIT License.
// License text available at https://opensource.org/licenses/MIT
'use strict';
var assert = require('assert');
var ModelBuilder = require('..').ModelBuilder;
var DataSource = require('../').DataSource;
@ -14,19 +20,18 @@ var json = {
city: 'San Jose',
state: 'CA',
zipcode: '95131',
country: 'US'
country: 'US',
},
friends: ['John', 'Mary'],
emails: [
{label: 'work', id: 'x@sample.com'},
{label: 'home', id: 'x@home.com'}
{label: 'home', id: 'x@home.com'},
],
tags: []
tags: [],
};
describe('Introspection of model definitions from JSON', function () {
it('should handle simple types', function () {
describe('Introspection of model definitions from JSON', function() {
it('should handle simple types', function() {
assert.equal(introspectType('123'), 'string');
assert.equal(introspectType(true), 'boolean');
assert.equal(introspectType(false), 'boolean');
@ -34,7 +39,7 @@ describe('Introspection of model definitions from JSON', function () {
assert.equal(introspectType(new Date()), 'date');
});
it('should handle array types', function () {
it('should handle array types', function() {
var type = introspectType(['123']);
assert.deepEqual(type, ['string'], 'type should be ["string"]');
type = introspectType([1]);
@ -49,12 +54,12 @@ describe('Introspection of model definitions from JSON', function () {
assert.equal(type, 'array');
});
it('should return Any for null or undefined', function () {
it('should return Any for null or undefined', function() {
assert.equal(introspectType(null), ModelBuilder.Any);
assert.equal(introspectType(undefined), ModelBuilder.Any);
});
it('should return a schema for object', function () {
it('should return a schema for object', function() {
var json = {a: 'str', b: 0, c: true};
var type = introspectType(json);
assert.equal(type.a, 'string');
@ -62,7 +67,7 @@ describe('Introspection of model definitions from JSON', function () {
assert.equal(type.c, 'boolean');
});
it('should handle nesting objects', function () {
it('should handle nesting objects', function() {
var json = {a: 'str', b: 0, c: true, d: {x: 10, y: 5}};
var type = introspectType(json);
assert.equal(type.a, 'string');
@ -72,7 +77,7 @@ describe('Introspection of model definitions from JSON', function () {
assert.equal(type.d.y, 'number');
});
it('should handle nesting arrays', function () {
it('should handle nesting arrays', function() {
var json = {a: 'str', b: 0, c: true, d: [1, 2]};
var type = introspectType(json);
assert.equal(type.a, 'string');
@ -81,7 +86,7 @@ describe('Introspection of model definitions from JSON', function () {
assert.deepEqual(type.d, ['number']);
});
it('should build a model from the introspected schema', function (done) {
it('should build a model from the introspected schema', function(done) {
var copy = traverse(json).clone();
var schema = introspectType(json);
@ -98,7 +103,7 @@ describe('Introspection of model definitions from JSON', function () {
done();
});
it('should build a model using buildModelFromInstance', function (done) {
it('should build a model using buildModelFromInstance', function(done) {
var copy = traverse(json).clone();
var builder = new ModelBuilder();
@ -110,7 +115,7 @@ describe('Introspection of model definitions from JSON', function () {
done();
});
it('should build a model using DataSource.buildModelFromInstance', function (done) {
it('should build a model using DataSource.buildModelFromInstance', function(done) {
var copy = traverse(json).clone();
var builder = new DataSource('memory');
@ -124,6 +129,5 @@ describe('Introspection of model definitions from JSON', function () {
assert.deepEqual(obj, copy);
done();
});
});

View File

@ -1,13 +1,19 @@
// Copyright IBM Corp. 2013,2016. All Rights Reserved.
// Node module: loopback-datasource-juggler
// This file is licensed under the MIT License.
// License text available at https://opensource.org/licenses/MIT
'use strict';
// This test written in mocha+should.js
var should = require('./init.js');
var Schema = require('../').Schema;
var ModelBuilder = require('../').ModelBuilder;
describe('JSON property', function () {
describe('JSON property', function() {
var dataSource, Model;
it('should be defined', function () {
it('should be defined', function() {
dataSource = getSchema();
Model = dataSource.define('Model', {propertyName: ModelBuilder.JSON});
var m = new Model;
@ -15,22 +21,22 @@ describe('JSON property', function () {
should.not.exist(m.propertyName);
});
it('should accept JSON in constructor and return object', function () {
it('should accept JSON in constructor and return object', function() {
var m = new Model({
propertyName: '{"foo": "bar"}'
propertyName: '{"foo": "bar"}',
});
m.propertyName.should.be.an.Object;
m.propertyName.foo.should.equal('bar');
});
it('should accept object in setter and return object', function () {
it('should accept object in setter and return object', function() {
var m = new Model;
m.propertyName = {"foo": "bar"};
m.propertyName = {'foo': 'bar'};
m.propertyName.should.be.an.Object;
m.propertyName.foo.should.equal('bar');
});
it('should accept string in setter and return string', function () {
it('should accept string in setter and return string', function() {
var m = new Model;
m.propertyName = '{"foo": "bar"}';
m.propertyName.should.be.a.String;

12
test/kv-memory.js Normal file
View File

@ -0,0 +1,12 @@
'use strict';
var kvMemory = require('../lib/connectors/kv-memory');
var DataSource = require('..').DataSource;
describe('KeyValue-Memory connector', function() {
var dataSourceFactory = function() {
return new DataSource({connector: kvMemory});
};
require('./kvao.suite')(dataSourceFactory);
});

33
test/kvao.suite.js Normal file
View File

@ -0,0 +1,33 @@
'use strict';
var debug = require('debug')('test');
var extend = require('util')._extend;
var fs = require('fs');
var path = require('path');
if (!global.Promise)
global.Promise = require('bluebird');
module.exports = function(dataSourceFactory, connectorCapabilities) {
connectorCapabilities = extend({
// Even when the backend supports millisecond precision,
// it's better to use intervals at least 10ms long in the tests
ttlPrecision: 10,
}, connectorCapabilities);
describe('KeyValue API', function loadAllTestFiles() {
var testRoot = path.resolve(__dirname, 'kvao');
var testFiles = fs.readdirSync(testRoot);
testFiles = testFiles.filter(function(it) {
return !!require.extensions[path.extname(it).toLowerCase()] &&
/\.suite\.[^.]+$/.test(it);
});
for (var ix in testFiles) {
var name = testFiles[ix];
var fullPath = path.resolve(testRoot, name);
debug('Loading test suite %s (%s)', name, fullPath);
require(fullPath)(dataSourceFactory, connectorCapabilities);
}
});
};

23
test/kvao/_helpers.js Normal file
View File

@ -0,0 +1,23 @@
'use strict';
var Promise = require('bluebird');
exports.givenCacheItem = function(dataSourceFactory) {
var dataSource = dataSourceFactory();
return dataSource.createModel('CacheItem', {
key: String,
value: 'any',
});
};
exports.givenKeys = function(Model, keys, cb) {
var p = Promise.all(
keys.map(function(k) {
return Model.set(k, 'value-' + k);
})
);
if (cb) {
p = p.then(function(r) { cb(null, r); }, cb);
}
return p;
};

66
test/kvao/expire.suite.js Normal file
View File

@ -0,0 +1,66 @@
'use strict';
var bdd = require('../helpers/bdd-if');
var should = require('should');
var helpers = require('./_helpers');
var Promise = require('bluebird');
module.exports = function(dataSourceFactory, connectorCapabilities) {
// While we support millisecond precision, for the purpose of tests
// it's better to use intervals at least 10ms long.
var ttlPrecision = connectorCapabilities.ttlPrecision || 10;
var canExpire = connectorCapabilities.canExpire !== false;
bdd.describeIf(canExpire, 'expire', function() {
var CacheItem;
beforeEach(function unpackContext() {
CacheItem = helpers.givenCacheItem(dataSourceFactory);
});
it('sets key ttl - Callback API', function(done) {
CacheItem.set('a-key', 'a-value', function(err) {
if (err) return done(err);
CacheItem.expire('a-key', ttlPrecision, function(err) {
if (err) return done(err);
setTimeout(function() {
CacheItem.get('a-key', function(err, value) {
if (err) return done(err);
should.equal(value, null);
done();
});
}, 2 * ttlPrecision);
});
});
});
it('sets key ttl - Promise API', function() {
return Promise.resolve(CacheItem.set('a-key', 'a-value'))
.then(function() { return CacheItem.expire('a-key', ttlPrecision); })
.delay(2 * ttlPrecision)
.then(function() { return CacheItem.get('a-key'); })
.then(function(value) { should.equal(value, null); });
});
it('returns error when expiring a key that has expired', function() {
return Promise.resolve(CacheItem.set('expired-key', 'a-value', ttlPrecision))
.delay(2 * ttlPrecision)
.then(function() { return CacheItem.expire('expired-key', 1000); })
.then(
function() { throw new Error('expire() should have failed'); },
function(err) {
err.message.should.match(/expired-key/);
err.should.have.property('statusCode', 404);
});
});
it('returns error when key does not exist', function() {
return CacheItem.expire('key-does-not-exist', ttlPrecision).then(
function() { throw new Error('expire() should have failed'); },
function(err) {
err.message.should.match(/key-does-not-exist/);
err.should.have.property('statusCode', 404);
});
});
});
};

105
test/kvao/get-set.suite.js Normal file
View File

@ -0,0 +1,105 @@
'use strict';
var should = require('should');
var helpers = require('./_helpers');
var Promise = require('bluebird');
module.exports = function(dataSourceFactory, connectorCapabilities) {
var TTL_PRECISION = connectorCapabilities.ttlPrecision;
describe('get/set', function() {
var CacheItem;
beforeEach(function unpackContext() {
CacheItem = helpers.givenCacheItem(dataSourceFactory);
});
it('works for string values - Callback API', function(done) {
CacheItem.set('a-key', 'a-value', function(err) {
if (err) return done(err);
CacheItem.get('a-key', function(err, value) {
if (err) return done(err);
should.equal(value, 'a-value');
done();
});
});
});
it('works for string values - Promise API', function() {
return CacheItem.set('a-key', 'a-value')
.then(function() { return CacheItem.get('a-key'); })
.then(function(value) { should.equal(value, 'a-value'); });
});
it('works for Object values', function() {
return CacheItem.set('a-key', {a: 1, b: 2})
.then(function() { return CacheItem.get('a-key'); })
.then(function(value) { value.should.eql({a: 1, b: 2}); });
});
it('works for Buffer values', function() {
return CacheItem.set('a-key', new Buffer([1, 2, 3]))
.then(function() { return CacheItem.get('a-key'); })
.then(function(value) { value.should.eql(new Buffer([1, 2, 3])); });
});
it('works for Date values', function() {
return CacheItem.set('a-key', new Date('2016-08-03T11:53:03.470Z'))
.then(function() { return CacheItem.get('a-key'); })
.then(function(value) {
value.should.be.instanceOf(Date);
value.toISOString().should.equal('2016-08-03T11:53:03.470Z');
});
});
it('works for Number values - integers', function() {
return CacheItem.set('a-key', 12345)
.then(function() { return CacheItem.get('a-key'); })
.then(function(value) { value.should.equal(12345); });
});
it('works for Number values - floats', function() {
return CacheItem.set('a-key', 12.345)
.then(function() { return CacheItem.get('a-key'); })
.then(function(value) { value.should.equal(12.345); });
});
it('works for Boolean values', function() {
return CacheItem.set('a-key', false)
.then(function() { return CacheItem.get('a-key'); })
.then(function(value) { value.should.equal(false); });
});
it('honours options.ttl', function() {
return Promise.resolve(CacheItem.set('a-key', 'a-value', {ttl: TTL_PRECISION}))
.delay(2 * TTL_PRECISION)
.then(function() { return CacheItem.get('a-key'); })
.then(function(value) { should.equal(value, null); });
});
describe('get', function() {
it('returns "null" when key does not exist', function() {
return CacheItem.get('key-does-not-exist')
.then(function(value) { should.equal(value, null); });
});
});
describe('set', function() {
it('converts numeric options arg to options.ttl', function() {
return Promise.resolve(CacheItem.set('a-key', 'a-value', TTL_PRECISION))
.delay(2 * TTL_PRECISION)
.then(function() { return CacheItem.get('a-key'); })
.then(function(value) { should.equal(value, null); });
});
it('resets TTL timer', function() {
return Promise.resolve(CacheItem.set('a-key', 'a-value', {ttl: TTL_PRECISION}))
.then(function() {
return CacheItem.set('a-key', 'another-value'); // no TTL
})
.delay(2 * TTL_PRECISION)
.then(function() { return CacheItem.get('a-key'); })
.then(function(value) { should.equal(value, 'another-value'); });
});
});
});
};

View File

@ -0,0 +1,51 @@
'use strict';
var asyncIterators = require('async-iterators');
var bdd = require('../helpers/bdd-if');
var helpers = require('./_helpers');
var Promise = require('bluebird');
var should = require('should');
var toArray = Promise.promisify(asyncIterators.toArray);
module.exports = function(dataSourceFactory, connectorCapabilities) {
var canIterateKeys = connectorCapabilities.canIterateKeys !== false;
bdd.describeIf(canIterateKeys, 'iterateKeys', function() {
var CacheItem;
beforeEach(function unpackContext() {
CacheItem = helpers.givenCacheItem(dataSourceFactory);
});
it('returns AsyncIterator covering all keys', function() {
return helpers.givenKeys(CacheItem, ['key1', 'key2'])
.then(function() {
var it = CacheItem.iterateKeys();
should(it).have.property('next');
return toArray(it);
})
.then(function(keys) {
keys.sort();
should(keys).eql(['key1', 'key2']);
});
});
it('returns AsyncIterator supporting Promises', function() {
var iterator;
return helpers.givenKeys(CacheItem, ['key'])
.then(function() {
iterator = CacheItem.iterateKeys();
return iterator.next();
})
.then(function(key) {
should(key).equal('key');
return iterator.next();
})
.then(function(key) {
// Note: AsyncIterator contract requires `undefined` to signal
// the end of the sequence. Other false-y values like `null`
// don't work.
should(key).equal(undefined);
});
});
});
};

109
test/kvao/keys.suite.js Normal file
View File

@ -0,0 +1,109 @@
'use strict';
var bdd = require('../helpers/bdd-if');
var helpers = require('./_helpers');
var Promise = require('bluebird');
var should = require('should');
module.exports = function(dataSourceFactory, connectorCapabilities) {
var canIterateKeys = connectorCapabilities.canIterateKeys !== false;
bdd.describeIf(canIterateKeys, 'keys', function() {
var CacheItem;
beforeEach(function unpackContext() {
CacheItem = helpers.givenCacheItem(dataSourceFactory);
CacheItem.sortedKeys = function(filter, options) {
return this.keys(filter, options).then(function(keys) {
keys.sort();
return keys;
});
};
});
it('returns all keys - Callback API', function(done) {
helpers.givenKeys(CacheItem, ['key1', 'key2'], function(err) {
if (err) return done(err);
CacheItem.keys(function(err, keys) {
if (err) return done(err);
keys.sort();
should(keys).eql(['key1', 'key2']);
done();
});
});
});
it('returns all keys - Promise API', function() {
return helpers.givenKeys(CacheItem, ['key1', 'key2'])
.then(function() {
return CacheItem.keys();
})
.then(function(keys) {
keys.sort();
should(keys).eql(['key1', 'key2']);
});
});
it('returns keys of the given model only', function() {
var AnotherModel = CacheItem.dataSource.createModel('AnotherModel');
return helpers.givenKeys(CacheItem, ['key1', 'key2'])
.then(function() {
return helpers.givenKeys(AnotherModel, ['otherKey1', 'otherKey2']);
})
.then(function() {
return CacheItem.sortedKeys();
})
.then(function(keys) {
should(keys).eql(['key1', 'key2']);
});
});
var largeKeySets = connectorCapabilities.canIterateLargeKeySets !== false;
bdd.itIf(largeKeySets, 'handles large key set', function() {
var expectedKeys = [];
for (var ix = 0; ix < 1000; ix++)
expectedKeys.push('key-' + ix);
expectedKeys.sort();
return helpers.givenKeys(CacheItem, expectedKeys)
.then(function() {
return CacheItem.sortedKeys();
})
.then(function(keys) {
should(keys).eql(expectedKeys);
});
});
context('with "filter.match"', function() {
beforeEach(function createTestData() {
return helpers.givenKeys(CacheItem, [
'hallo',
'hello',
'hxllo',
'hllo',
'heeello',
'foo',
'bar',
]);
});
it('supports "?" operator', function() {
return CacheItem.sortedKeys({match: 'h?llo'}).then(function(keys) {
should(keys).eql(['hallo', 'hello', 'hxllo']);
});
});
it('supports "*" operator', function() {
return CacheItem.sortedKeys({match: 'h*llo'}).then(function(keys) {
should(keys).eql(['hallo', 'heeello', 'hello', 'hllo', 'hxllo']);
});
});
it('handles no matches found', function() {
return CacheItem.sortedKeys({match: 'not-found'})
.then(function(keys) {
should(keys).eql([]);
});
});
});
});
};

79
test/kvao/ttl.suite.js Normal file
View File

@ -0,0 +1,79 @@
'use strict';
var bdd = require('../helpers/bdd-if');
var should = require('should');
var helpers = require('./_helpers');
var Promise = require('bluebird');
module.exports = function(dataSourceFactory, connectorCapabilities) {
var TTL_PRECISION = connectorCapabilities.ttlPrecision;
// Use ~1s for stores with precision of 1 ms,
// about 3s for stores with precision of 1s.
var INITIAL_TTL = Math.max(TTL_PRECISION + 1000, TTL_PRECISION * 3);
// A small delay to allow the backend to process the request, run any
// TTL/expire checks, etc. Use 1ms for backends supporting sub-10ms
// resolution to ensure the delay is not too short..
var SMALL_DELAY = Math.max(1, Math.floor(TTL_PRECISION / 10));
var canQueryTtl = connectorCapabilities.canQueryTtl !== false;
bdd.describeIf(canQueryTtl, 'ttl', function() {
var CacheItem;
beforeEach(function unpackContext() {
CacheItem = helpers.givenCacheItem(dataSourceFactory);
});
it('gets TTL when key with unexpired TTL exists - Promise API',
function() {
return Promise.resolve(
CacheItem.set('a-key', 'a-value', {ttl: INITIAL_TTL}))
.delay(SMALL_DELAY)
.then(function() { return CacheItem.ttl('a-key'); })
.then(function(ttl) { ttl.should.be.within(1, INITIAL_TTL); });
});
it('gets TTL when key with unexpired TTL exists - Callback API',
function(done) {
CacheItem.set('a-key', 'a-value', {ttl: INITIAL_TTL}, function(err) {
if (err) return done(err);
CacheItem.ttl('a-key', function(err, ttl) {
if (err) return done(err);
ttl.should.be.within(1, INITIAL_TTL);
done();
});
});
});
it('succeeds when key without TTL exists', function() {
return CacheItem.set('a-key', 'a-value')
.then(function() { return CacheItem.ttl('a-key'); })
.then(function(ttl) { should.not.exist(ttl); });
});
it('fails when getting TTL for a key with expired TTL', function() {
return Promise.resolve(
CacheItem.set('expired-key', 'a-value', {ttl: TTL_PRECISION}))
.delay(2 * TTL_PRECISION)
.then(function() {
return CacheItem.ttl('expired-key');
})
.then(
function() { throw new Error('ttl() should have failed'); },
function(err) {
err.message.should.match(/expired-key/);
err.should.have.property('statusCode', 404);
});
});
it('fails when key does not exist', function() {
return CacheItem.ttl('key-does-not-exist').then(
function() { throw new Error('ttl() should have failed'); },
function(err) {
err.message.should.match(/key-does-not-exist/);
err.should.have.property('statusCode', 404);
});
});
});
};

View File

@ -1,10 +1,16 @@
// Copyright IBM Corp. 2013,2016. All Rights Reserved.
// Node module: loopback-datasource-juggler
// This file is licensed under the MIT License.
// License text available at https://opensource.org/licenses/MIT
'use strict';
// This test written in mocha+should.js
var should = require('./init.js');
var loopbackData = require('../');
describe('loopback-datasource-juggler', function () {
it('should expose version', function () {
describe('loopback-datasource-juggler', function() {
it('should expose version', function() {
loopbackData.version.should.equal(require('../package.json').version);
});
});

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

Some files were not shown because too many files have changed in this diff Show More