Compare commits

..
701 Commits
Author SHA1 Message Date
Arthur Cinader 61ab8c90f7 3.4.3 changelog and version bump 2019-06-13 09:51:07 -07:00
Arthur Cinader c9619bc525 Use master's travis 2019-06-13 09:37:42 -07:00
Arthur Cinader 0dd30a5051 Add 3.4.2 Changelog 2019-06-13 08:50:04 -07:00
Arthur Cinader e53514aa0f Get current changelog from master 2019-06-13 08:44:06 -07:00
Arthur Cinader f71db83c9c update version number 2019-06-13 08:40:48 -07:00
Diamond Lewis 4a71e118bf Update Dockerfile (#5676) 2019-06-13 08:36:41 -07:00
Diamond Lewis 7801b7d9ab Merge pull request from GHSA-2479-qvv7-47qq
* Failing test

* provide fix

* clearer test

* failing expect
2019-06-13 08:32:33 -07:00
Arthur Cinader e18b9a5e56 Prepare for 3.4.0 release (#5606)
* Prepare for 3.4.0 release

* Update CHANGELOG.md
2019-05-22 12:46:23 -07:00
dependabot[bot] e9b8752c3f Bump mongodb from 3.2.4 to 3.2.5 (#5604)
* Bump mongodb from 3.2.4 to 3.2.5

Bumps [mongodb](https://github.com/mongodb/node-mongodb-native) from 3.2.4 to 3.2.5.
- [Release notes](https://github.com/mongodb/node-mongodb-native/releases)
- [Changelog](https://github.com/mongodb/node-mongodb-native/blob/master/HISTORY.md)
- [Commits](https://github.com/mongodb/node-mongodb-native/compare/v3.2.4...v3.2.5)

Signed-off-by: dependabot[bot] <support@dependabot.com>

* Tweak expected error message in a test to match a change
in the underlying package.
see: https://github.com/mongodb-js/mongodb-core/commit/83e224b

cc: @davimacedo
2019-05-21 22:33:31 +00:00
greenkeeper[bot] 46408ee5e8 Update babel7 to the latest version 🚀 (#5605)
* chore(package): update @babel/core to version 7.4.5

* chore(package): update @babel/preset-env to version 7.4.5

* chore(package): update lockfile package-lock.json
2019-05-21 21:14:52 +00:00
greenkeeper[bot] 2c524604df Update express to the latest version 🚀 (#5600)
* fix(package): update express to version 4.17.0

* chore(package): update lockfile package-lock.json
2019-05-17 02:30:48 +00:00
Antonio Davi Macedo Coelho de Castro 2da2a27373 Fixing Redis adapter tests (#5599) 2019-05-16 15:26:45 -07:00
greenkeeper[bot] dc7d3f149f Update eslint-plugin-flowtype to the latest version 🚀 (#5596)
* chore(package): update eslint-plugin-flowtype to version 3.9.0

* chore(package): update lockfile package-lock.json
2019-05-15 19:04:28 +00:00
greenkeeper[bot] 07ff4112d4 Update mime to the latest version 🚀 (#5595)
* fix(package): update mime to version 2.4.3

* chore(package): update lockfile package-lock.json
2019-05-15 14:43:14 +00:00
dependabot[bot] 47a86be7ba Bump husky from 2.2.0 to 2.3.0 (#5594)
Bumps [husky](https://github.com/typicode/husky) from 2.2.0 to 2.3.0.
- [Release notes](https://github.com/typicode/husky/releases)
- [Changelog](https://github.com/typicode/husky/blob/master/CHANGELOG.md)
- [Commits](https://github.com/typicode/husky/compare/v2.2.0...v2.3.0)

Signed-off-by: dependabot[bot] <support@dependabot.com>
2019-05-15 10:35:49 +00:00
greenkeeper[bot] d84f56bb0e Update lint-staged to the latest version 🚀 (#5593)
* chore(package): update lint-staged to version 8.1.7

* chore(package): update lockfile package-lock.json
2019-05-15 07:14:03 +00:00
Antonio Davi Macedo Coelho de Castro afa74d655d Futzing with read preference (#3963)
* allow setting readpreference when using rest api.

* take out partially complete unit test.

* oops. nit

* Include read preference option for find directly from api and adding few more tests

* Adding catch for all tests

* Keep same check for get and find

* Turn read preference case insensitive

* Includes and subqueries read preferences through API

* Fixing bugs regarding changes that were done in master branch during the last year

* Changing behavior to make includeReadPreference and subqueryReadPreference to follow readPreference by default
2019-05-14 12:58:02 -07:00
Antonio Davi Macedo Coelho de Castro 893f1d376e Remove test delays (#5579)
* Changing __indexBuildCompletionCallbackForTests callback to serverStartComplete

* Improving serverStartComplete callback to avoid production unhandled promise rejection

* Add test to check inexistence of unhandled promise rejection on server fail

* Removing some hooks delays

* Removing delay after reconfigureServer

* Improving code style
2019-05-14 11:34:51 -07:00
dependabot[bot] 2f161c25cd Bump prettier from 1.17.0 to 1.17.1 (#5590)
Bumps [prettier](https://github.com/prettier/prettier) from 1.17.0 to 1.17.1.
- [Release notes](https://github.com/prettier/prettier/releases)
- [Changelog](https://github.com/prettier/prettier/blob/master/CHANGELOG.md)
- [Commits](https://github.com/prettier/prettier/compare/1.17.0...1.17.1)

Signed-off-by: dependabot[bot] <support@dependabot.com>
2019-05-14 10:33:23 +00:00
William George 5a8a9472e9 Remove userSensitiveFields default value. (#5588) 2019-05-13 17:14:31 -07:00
greenkeeper[bot] 3bb1796e12 Update jsdoc to the latest version 🚀 (#5586)
* chore(package): update jsdoc to version 3.6.2

* chore(package): update lockfile package-lock.json
2019-05-12 17:15:31 +00:00
greenkeeper[bot] 908270aba1 Update pg-promise to the latest version 🚀 (#5585)
* fix(package): update pg-promise to version 8.7.2

* chore(package): update lockfile package-lock.json
2019-05-12 07:51:55 +00:00
Diamond LewisandTom Fox 63b7146a3c Update README.md (#5583)
* Update README.md

* bump Live Queries

* Apply suggestions from code review

Co-Authored-By: Tom Fox <tomfox@surprises.io>
2019-05-11 19:14:22 -05:00
Diamond Lewis 0ce4eeae72 LiveQuery: Add options for Redis (#5584)
Closes: https://github.com/parse-community/parse-server/issues/5387
2019-05-11 19:13:41 -05:00
Diamond Lewis b9c936f594 Add Docs for Server Config Definitions (#5581)
* Add Docs for Server Config Definitions

* protected field and sort alphabetically

* Add links

* nits
2019-05-11 15:25:31 -05:00
Antonio Davi Macedo Coelho de Castro 90c81c1750 Validates permission before calling beforeSave trigger (#5546)
* Test to reproduce the problem

* Validating update before calling beforeSave trigger

* Fixing lint

* Commenting code

* Improving the code
2019-05-11 10:37:27 -07:00
greenkeeper[bot] 2cc21bf1f2 Update eslint-plugin-flowtype to the latest version 🚀 (#5582)
* chore(package): update eslint-plugin-flowtype to version 3.8.2

* chore(package): update lockfile package-lock.json
2019-05-11 09:06:43 +00:00
Diamond Lewis b4d915bbbb Support direct access server option (#5550)
* Support direct access config

test options

* add test

* fix test

* fix definitions

* improve docs

* Update .travis.yml

* Revert "Update .travis.yml"

This reverts commit 407f138495.
2019-05-10 14:34:19 -05:00
greenkeeper[bot] f15360cb12 Update pg-promise to the latest version 🚀 (#5580)
* fix(package): update pg-promise to version 8.7.1

* chore(package): update lockfile package-lock.json
2019-05-10 19:21:29 +00:00
greenkeeper[bot] 31c9728b3a Update nyc to the latest version 🚀 (#5576)
* chore(package): update nyc to version 14.1.1

* chore(package): update lockfile package-lock.json
2019-05-10 03:07:24 +00:00
Diamond Lewis 4c996baf72 Update Travis CI Cache (#5575)
`prepare` causes `npm ci` to be used. `.npm` is the recommended cache directory. Should speed up build times.

https://docs.npmjs.com/cli/ci
2019-05-09 22:04:40 -05:00
greenkeeper[bot] 8a543bfda4 Update pg-promise to the latest version 🚀 (#5574)
* fix(package): update pg-promise to version 8.7.0

* chore(package): update lockfile package-lock.json
2019-05-09 20:16:46 +00:00
Arthur Cinader 87da62bca2 Logger: Fix timestamp and format (#5571)
* remove no-op config of logger

* add a test to check on the timestamp

* add a test to verify that we
get non json console loggging by default

* configure transports to include
timestamps in files

* Add failing test to confirm that WinstonLoggerAdapter
is not filtering on level.

* actually fix the test to refelect the facth that this isn't the problem

* Remove bogus date ranges that are now failing
becuase we have timestamps.
2019-05-09 14:58:54 -05:00
Antonio Davi Macedo Coelho de Castro 81ecf2fd74 Fix jasmine 3.4 (#5573)
* Fix failing tests

* just ignore the test for now.

* Bumping jasmine

* Fix pg unhandled exception

* Improving the way the test is fixed

* Fix unhandled failed promise in postgres test

* Solving unhandled promise fail on redis test

* Returning the excluded test

* Fixing package-lock

* Fix unhandled promise from redis test
2019-05-09 09:12:30 -07:00
greenkeeper[bot] 9232fcc601 Update mongodb to the latest version 🚀 (#5572)
* fix(package): update mongodb to version 3.2.4

* chore(package): update lockfile package-lock.json
2019-05-09 00:12:47 +00:00
dependabot[bot] dabdf3b8a5 Bump eslint-plugin-flowtype from 3.7.0 to 3.8.1 (#5570)
Bumps [eslint-plugin-flowtype](https://github.com/gajus/eslint-plugin-flowtype) from 3.7.0 to 3.8.1.
- [Release notes](https://github.com/gajus/eslint-plugin-flowtype/releases)
- [Commits](https://github.com/gajus/eslint-plugin-flowtype/compare/v3.7.0...v3.8.1)

Signed-off-by: dependabot[bot] <support@dependabot.com>
2019-05-08 10:32:03 +00:00
greenkeeper[bot] cc3f210bf6 Update flow-bin to the latest version 🚀 (#5567)
* chore(package): update flow-bin to version 0.98.1

* chore(package): update lockfile package-lock.json
2019-05-06 17:19:12 +00:00
greenkeeper[bot] f325224ffb Update jsdoc to the latest version 🚀 (#5565)
* chore(package): update jsdoc to version 3.6.1

* chore(package): update lockfile package-lock.json
2019-05-05 19:54:46 +00:00
greenkeeper[bot] 1e6b48a754 Update jsdoc to the latest version 🚀 (#5563)
* chore(package): update jsdoc to version 3.6.0

* chore(package): update lockfile package-lock.json
2019-05-05 00:26:15 +00:00
Diamond Lewis 87b0093873 (chore): Fix broken codecov (#5554) 2019-05-03 18:01:10 -07:00
dependabot[bot] 7fd2a91503 Bump lint-staged from 8.1.5 to 8.1.6 (#5559)
Bumps [lint-staged](https://github.com/okonet/lint-staged) from 8.1.5 to 8.1.6.
- [Release notes](https://github.com/okonet/lint-staged/releases)
- [Commits](https://github.com/okonet/lint-staged/compare/v8.1.5...v8.1.6)

Signed-off-by: dependabot[bot] <support@dependabot.com>
2019-05-03 10:52:00 +00:00
dependabot[bot] fdf08c4f09 Bump body-parser from 1.18.3 to 1.19.0 (#5531)
Bumps [body-parser](https://github.com/expressjs/body-parser) from 1.18.3 to 1.19.0.
- [Release notes](https://github.com/expressjs/body-parser/releases)
- [Changelog](https://github.com/expressjs/body-parser/blob/master/HISTORY.md)
- [Commits](https://github.com/expressjs/body-parser/compare/1.18.3...1.19.0)

Signed-off-by: dependabot[bot] <support@dependabot.com>
2019-05-02 19:47:08 +00:00
greenkeeper[bot] e5f34c3393 Update nyc to the latest version 🚀 (#5555)
* chore(package): update nyc to version 14.1.0

* chore(package): update lockfile package-lock.json
2019-05-02 19:27:37 +00:00
Diamond Lewis e0a25d9030 Fix flaky test. Give em a little more time. (#5557)
Similar to https://github.com/parse-community/parse-server/pull/3453
2019-05-02 13:59:01 -05:00
Diamond Lewis 005fd78275 PG: Fix updating mixed array (#5552)
* PG: Fix updating mixed array

* Revert "PG: Fix updating mixed array"

This reverts commit 5a441413c0.

* simple fix
2019-05-02 12:44:17 -05:00
greenkeeper[bot] d4f4667008 Update eslint-plugin-flowtype to the latest version 🚀 (#5556)
* chore(package): update eslint-plugin-flowtype to version 3.7.0

* chore(package): update lockfile package-lock.json
2019-05-02 16:22:19 +00:00
greenkeeper[bot] 1b0d20d53c Update husky to the latest version 🚀 (#5553)
* chore(package): update husky to version 2.2.0

* chore(package): update lockfile package-lock.json
2019-05-02 04:35:38 +00:00
Antonio Davi Macedo Coelho de Castro 8f0e7f3bcc Fix test that is failing the build (#5551) 2019-05-01 17:29:53 -05:00
Diamond Lewis 9594c4b59f Add useNewUrlParser options to GridFSBucketAdapter (#5548)
* Add useNewUrlParser options to GridFSBucketAdapter

* allow overriding default
2019-05-01 00:44:10 -05:00
Diamond Lewis 057fc40c1d Postgres: Query notEqualTo GeoPoint (#5549)
* Postgres: Query notEqualTo GeoPoint

* remove templated strings
2019-04-30 23:29:44 -05:00
ananfang fa97df5f4e Decode Date JSON value at LiveQuery (#5540) 2019-04-30 13:00:23 -05:00
greenkeeper[bot] 2e2ee06279 Update ws to the latest version 🚀 (#5547)
* fix(package): update ws to version 7.0.0

* chore(package): update lockfile package-lock.json
2019-04-30 16:42:31 +00:00
Tom Fox 99a1b13e23 Improve contributing guide (#5543)
* Update CONTRIBUTING.md

* add note about installing code command
2019-04-29 18:47:49 +01:00
dependabot[bot] 748a9bf52d Bump winston-daily-rotate-file from 3.8.0 to 3.9.0 (#5544)
Bumps [winston-daily-rotate-file](https://github.com/winstonjs/winston-daily-rotate-file) from 3.8.0 to 3.9.0.
- [Release notes](https://github.com/winstonjs/winston-daily-rotate-file/releases)
- [Commits](https://github.com/winstonjs/winston-daily-rotate-file/compare/v3.8.0...v3.9.0)

Signed-off-by: dependabot[bot] <support@dependabot.com>
2019-04-29 10:32:49 +00:00
Tom Fox 022b33e83c Remove video series reference (#5542) 2019-04-27 19:32:07 +01:00
Antonio Davi Macedo Coelho de Castro db994ed473 Aggregate supports group by date fields (#5538)
* it actually supports group by date fields

* Changing the field name again to see Travis logs

* Adding match stage to the test

* Adding test for group by date fields on postgres
2019-04-26 20:33:03 -05:00
Antonio Davi Macedo Coelho de Castro 43c41925a2 Local tests failing for timezones <= -7 (#5536) 2019-04-26 18:26:20 -05:00
greenkeeper[bot] 9c56d5c7e9 Update babel7 to the latest version 🚀 (#5537)
* chore(package): update @babel/cli to version 7.4.4

* chore(package): update @babel/core to version 7.4.4

* chore(package): update @babel/plugin-proposal-object-rest-spread to version 7.4.4

* chore(package): update @babel/plugin-transform-flow-strip-types to version 7.4.4

* chore(package): update @babel/preset-env to version 7.4.4

* chore(package): update lockfile package-lock.json
2019-04-26 22:33:11 +00:00
Tom Fox b919c36db0 3.3.0 release notes & version bump (#5534)
* Update CHANGELOG.md

* bump version to 3.3.0
2019-04-26 22:22:36 +01:00
greenkeeper[bot] c70e015751 Update flow-bin to the latest version 🚀 (#5532)
* chore(package): update flow-bin to version 0.98.0

* chore(package): update lockfile package-lock.json
2019-04-26 20:43:13 +00:00
dependabot[bot] 3608b19b3a Bump parse from 2.3.2 to 2.4.0 (#5529)
Bumps [parse](https://github.com/parse-community/Parse-SDK-JS) from 2.3.2 to 2.4.0.
- [Release notes](https://github.com/parse-community/Parse-SDK-JS/releases)
- [Changelog](https://github.com/parse-community/Parse-SDK-JS/blob/master/CHANGELOG.md)
- [Commits](https://github.com/parse-community/Parse-SDK-JS/compare/2.3.2...2.4.0)

Signed-off-by: dependabot[bot] <support@dependabot.com>
2019-04-25 10:42:55 +00:00
Julien Quéré 7122ca05c4 Fix issue on count with Geo constraints and mongo (issue #5285) (#5286)
* Add a tests that fails due to issue #5285

* Make test code much simpler

* Fix #5285 by rewriting query (replacing $nearSphere by $geoWithin)

All credit goes to @dplewis !

* move logic to transform
2019-04-24 20:28:13 -05:00
greenkeeper[bot] 7e130c5421 Update husky to the latest version 🚀 (#5527)
* chore(package): update husky to version 2.1.0

* chore(package): update lockfile package-lock.json
2019-04-24 22:15:51 +00:00
greenkeeper[bot] f7bcfcea24 Update husky to the latest version 🚀 (#5526)
* chore(package): update husky to version 2.0.0

* chore(package): update lockfile package-lock.json
2019-04-24 16:53:18 +00:00
Omair Vaiyani a1e1cef6d2 Add beforeLogin trigger with support for auth providers (#5445)
* Add beforeLogin trigger with support for auth providers

* adjust comment that boxed off beforeLogin to a negative use-case only

* add internal error to help future maintainers regarding use of beforeLogin

* let beforeLogin accept className or constructor like other hook types

* add assertions for beforeLogin trigger className validation
2019-04-23 08:24:20 -07:00
dependabot[bot] 3e003ee9f4 Bump nyc from 13.1.0 to 14.0.0 (#5517)
Bumps [nyc](https://github.com/istanbuljs/nyc) from 13.1.0 to 14.0.0.
- [Release notes](https://github.com/istanbuljs/nyc/releases)
- [Changelog](https://github.com/istanbuljs/nyc/blob/master/CHANGELOG.md)
- [Commits](https://github.com/istanbuljs/nyc/compare/v13.1.0...v14.0.0)

Signed-off-by: dependabot[bot] <support@dependabot.com>
2019-04-16 10:54:55 +00:00
dependabot[bot] 828ae7e708 Bump mongodb from 3.2.2 to 3.2.3 (#5492)
Bumps [mongodb](https://github.com/mongodb/node-mongodb-native) from 3.2.2 to 3.2.3.
- [Release notes](https://github.com/mongodb/node-mongodb-native/releases)
- [Changelog](https://github.com/mongodb/node-mongodb-native/blob/master/HISTORY.md)
- [Commits](https://github.com/mongodb/node-mongodb-native/commits)

Signed-off-by: dependabot[bot] <support@dependabot.com>
2019-04-15 12:07:19 +00:00
dependabot[bot] 2998b2a1ca Bump eslint-plugin-flowtype from 3.5.0 to 3.6.1 (#5495)
Bumps [eslint-plugin-flowtype](https://github.com/gajus/eslint-plugin-flowtype) from 3.5.0 to 3.6.1.
- [Release notes](https://github.com/gajus/eslint-plugin-flowtype/releases)
- [Commits](https://github.com/gajus/eslint-plugin-flowtype/compare/v3.5.0...v3.6.1)

Signed-off-by: dependabot[bot] <support@dependabot.com>
2019-04-15 11:51:26 +00:00
dependabot[bot] 1f109de7a9 Bump mime from 2.4.0 to 2.4.2 (#5493)
Bumps [mime](https://github.com/broofa/node-mime) from 2.4.0 to 2.4.2.
- [Release notes](https://github.com/broofa/node-mime/releases)
- [Changelog](https://github.com/broofa/node-mime/blob/master/CHANGELOG.md)
- [Commits](https://github.com/broofa/node-mime/compare/v2.4.0...v2.4.2)

Signed-off-by: dependabot[bot] <support@dependabot.com>
2019-04-15 11:29:05 +00:00
dependabot[bot] c0b8de7a76 Bump prettier from 1.16.4 to 1.17.0 (#5512)
Bumps [prettier](https://github.com/prettier/prettier) from 1.16.4 to 1.17.0.
- [Release notes](https://github.com/prettier/prettier/releases)
- [Changelog](https://github.com/prettier/prettier/blob/master/CHANGELOG.md)
- [Commits](https://github.com/prettier/prettier/compare/1.16.4...1.17.0)

Signed-off-by: dependabot[bot] <support@dependabot.com>
2019-04-15 11:06:00 +00:00
dependabot[bot] 0bad5f40e4 Bump bcrypt from 3.0.5 to 3.0.6 (#5511)
Bumps [bcrypt](https://github.com/kelektiv/node.bcrypt.js) from 3.0.5 to 3.0.6.
- [Release notes](https://github.com/kelektiv/node.bcrypt.js/releases)
- [Changelog](https://github.com/kelektiv/node.bcrypt.js/blob/master/CHANGELOG.md)
- [Commits](https://github.com/kelektiv/node.bcrypt.js/compare/v3.0.5...v3.0.6)

Signed-off-by: dependabot[bot] <support@dependabot.com>
2019-04-15 10:57:57 +00:00
dependabot[bot] dc9434645b Bump flow-bin from 0.96.0 to 0.97.0 (#5510)
Bumps [flow-bin](https://github.com/flowtype/flow-bin) from 0.96.0 to 0.97.0.
- [Release notes](https://github.com/flowtype/flow-bin/releases)
- [Commits](https://github.com/flowtype/flow-bin/compare/v0.96.0...v0.97.0)

Signed-off-by: dependabot[bot] <support@dependabot.com>
2019-04-15 10:48:35 +00:00
dependabot[bot] 266db27c72 Bump pg-promise from 8.6.4 to 8.6.5 (#5509)
Bumps [pg-promise](https://github.com/vitaly-t/pg-promise) from 8.6.4 to 8.6.5.
- [Release notes](https://github.com/vitaly-t/pg-promise/releases)
- [Commits](https://github.com/vitaly-t/pg-promise/compare/8.6.4...8.6.5)

Signed-off-by: dependabot[bot] <support@dependabot.com>
2019-04-15 10:38:29 +00:00
Sam Ilic 6ffc41345f Winston 3 upgrade (#5496)
* ⚡ Release 3.1.3 (#5267)

* ⚡ Release 3.1.3

* Update CHANGELOG.md

* ⬆️ Bump winston and winston-daily-rotate-file

Bumps [winston](https://github.com/winstonjs/winston) and [winston-daily-rotate-file](https://github.com/winstonjs/winston-daily-rotate-file). These dependencies needed to be updated together.

Updates `winston` from 2.4.4 to 3.1.0
- [Release notes](https://github.com/winstonjs/winston/releases)
- [Changelog](https://github.com/winstonjs/winston/blob/master/CHANGELOG.md)
- [Commits](https://github.com/winstonjs/winston/compare/2.4.4...3.1.0)

Updates `winston-daily-rotate-file` from 1.7.2 to 3.5.1
- [Release notes](https://github.com/winstonjs/winston-daily-rotate-file/releases)
- [Commits](https://github.com/winstonjs/winston-daily-rotate-file/compare/v1.7.2...v3.5.1)

Signed-off-by: dependabot[bot] <support@dependabot.com>

* Rewrote WinstonLogger to work with winston 3.x api

* Changed winston logger test to use winston-transport

* Added winston-transport dependency

* Close and remove transports before adding them again

* Changed to strict equal

* Override adapter name

* Updated and added getLogs tests

* Bump winston and winston-daily-rotate-file

Bumps [winston](https://github.com/winstonjs/winston) and [winston-daily-rotate-file](https://github.com/winstonjs/winston-daily-rotate-file). These dependencies needed to be updated together.

Updates `winston` from 2.4.4 to 3.2.0
- [Release notes](https://github.com/winstonjs/winston/releases)
- [Changelog](https://github.com/winstonjs/winston/blob/master/CHANGELOG.md)
- [Commits](https://github.com/winstonjs/winston/compare/2.4.4...3.2.0)

Updates `winston-daily-rotate-file` from 1.7.2 to 3.6.0
- [Release notes](https://github.com/winstonjs/winston-daily-rotate-file/releases)
- [Commits](https://github.com/winstonjs/winston-daily-rotate-file/compare/v1.7.2...v3.6.0)

Signed-off-by: dependabot[bot] <support@dependabot.com>

* Fixed tests, updated parse logging

* Fixed tests, better error logging

* Fix failing tests

* Updates as per review
2019-04-14 18:03:33 -05:00
Arthur Cinader 943134812e Revert "Bump jasmine from 3.1.0 to 3.4.0" (#5506)
This reverts commit e396612254.
2019-04-14 17:40:59 -05:00
Müller Zsolt 019cf0a986 added an RFC 7662 compliant OAuth2 auth adapter (#4910)
* added an RFC 7662 compliant OAuth2 auth adapter

* forgot to add the actual auth adapter to the previous commit

* fixed lint errors

* * added test coverage
* changed option names in auth adapter from snake case to camel case
* added underscore prefix to helper function names
* merged consecutive logger calls into one call and use JSON.stringify() to convert JSON objects to strings
* changed error handling (ParseErrors are no longer thrown, but returned)

* added description of the "debug" option and added this option to the tests too

* added a check of the "debug" option to the unittests and replaced require() of the logger with an import (the former does not work correctly)

* added AuthAdapter based auth adapter runtime validation to src/Adapters/Auth/index.js, added capability to define arbitrary providernames with an "adapter" property in auth config, replaced various "var" keywords with "const" in oauth2.js

* incorporated changes requested by flovilmart (mainly that oauth2 is now not a standalone adapter, but can be selected by setting the "oauth2" property to true in auth config

* modified oauth2 adapter as requested by flovilmart

* bugfix: defaultAdapter can be null in loadAuthAdapter() of index.js (my change broke the tests)

* added TODO on need for a validateAdapter() to validate auth adapters

* test cases and cleanup
2019-04-11 11:05:55 -05:00
Tulsi Sapkota a3746cab00 Cannot change password when maxPasswordHistory is 1 (#5191)
* Negitive to zero and positive to same value

* add failing test
2019-04-10 10:12:09 -07:00
CoderickLamar c7eb7daeae Fix for count being very slow on large Parse Classes' collections (Postgres) (#5330)
* Changed count to be approximate. Should help with postgres slowness

* refactored last commit to only fall back to estimate if no complex query

* handlign variables correctly

* Trying again because it was casting to lowercase table names which doesnt work for us/

* syntax error

* Adding quotations to pg query

* hopefully final pg fix

* Postgres will now use an approximate count unless there is a more complex query specified

* handling edge case

* Fix for count being very slow on large Parse Classes' collections in Postgres. Replicating fix for Mongo in issue 5264

* Fixed silly spelling error resulting from copying over notes

* Lint fixes

* limiting results to 1 on approximation

* suppress test that we can no longer run for postgres

* removed tests from Postgres that no longer apply

* made changes requested by dplewis

* fixed count errors

* updated package.json

* removed test exclude for pg

* removed object types from method

* test disabled for postgres

* returned type

* add estimate count test

* fix mongo test
2019-04-08 17:59:15 -05:00
dependabot[bot] e396612254 Bump jasmine from 3.1.0 to 3.4.0
Bumps [jasmine](https://github.com/jasmine/jasmine-npm) from 3.1.0 to 3.4.0.
- [Release notes](https://github.com/jasmine/jasmine-npm/releases)
- [Commits](https://github.com/jasmine/jasmine-npm/compare/v3.1.0...v3.4.0)

Signed-off-by: dependabot[bot] <support@dependabot.com>
2019-04-04 10:21:49 +00:00
greenkeeper[bot] cf00dfa1c3 Update parse to the latest version 🚀 (#5486)
* fix(package): update parse to version 2.3.2

* chore(package): update lockfile package-lock.json
2019-04-04 03:10:35 +00:00
greenkeeper[bot] b208fbf6a9 Update eslint-plugin-flowtype to the latest version 🚀 (#5480)
* chore(package): update eslint-plugin-flowtype to version 3.5.0

* chore(package): update lockfile package-lock.json
2019-04-03 17:52:30 +00:00
dependabot[bot] d779144da1 Bump @babel/core from 7.4.0 to 7.4.3 (#5478)
Bumps [@babel/core](https://github.com/babel/babel) from 7.4.0 to 7.4.3.
- [Release notes](https://github.com/babel/babel/releases)
- [Changelog](https://github.com/babel/babel/blob/master/CHANGELOG.md)
- [Commits](https://github.com/babel/babel/compare/v7.4.0...v7.4.3)

Signed-off-by: dependabot[bot] <support@dependabot.com>
2019-04-03 11:32:53 +00:00
dependabot[bot] 3227b0f4ae Bump @babel/preset-env from 7.4.2 to 7.4.3 (#5477)
Bumps [@babel/preset-env](https://github.com/babel/babel) from 7.4.2 to 7.4.3.
- [Release notes](https://github.com/babel/babel/releases)
- [Changelog](https://github.com/babel/babel/blob/master/CHANGELOG.md)
- [Commits](https://github.com/babel/babel/compare/v7.4.2...v7.4.3)

Signed-off-by: dependabot[bot] <support@dependabot.com>
2019-04-03 11:02:28 +00:00
dependabot[bot] bfe60049b3 Bump @babel/cli from 7.2.3 to 7.4.3 (#5476)
Bumps [@babel/cli](https://github.com/babel/babel) from 7.2.3 to 7.4.3.
- [Release notes](https://github.com/babel/babel/releases)
- [Changelog](https://github.com/babel/babel/blob/master/CHANGELOG.md)
- [Commits](https://github.com/babel/babel/compare/v7.2.3...v7.4.3)

Signed-off-by: dependabot[bot] <support@dependabot.com>
2019-04-03 10:52:35 +00:00
dependabot[bot] 5174afd300 Bump @babel/plugin-proposal-object-rest-spread from 7.4.0 to 7.4.3 (#5475)
Bumps [@babel/plugin-proposal-object-rest-spread](https://github.com/babel/babel) from 7.4.0 to 7.4.3.
- [Release notes](https://github.com/babel/babel/releases)
- [Changelog](https://github.com/babel/babel/blob/master/CHANGELOG.md)
- [Commits](https://github.com/babel/babel/compare/v7.4.0...v7.4.3)

Signed-off-by: dependabot[bot] <support@dependabot.com>
2019-04-03 10:42:42 +00:00
greenkeeper[bot] 44189802e9 Update commander to the latest version 🚀 (#5473)
* fix(package): update commander to version 2.20.0

* chore(package): update lockfile package-lock.json
2019-04-03 01:06:43 +00:00
Georges Jamous 214aa2e450 using per-key basis queue (#5420)
* adding KeyPromiseQueue

* nit

* removing secondary object and using a tuple

* using array

* nits

* some tests

* Minor refinements

* removing old adapter

* dummy change, travis test not found

* travis test missing, dummy change

* revrting mistake

* reverting mistake

* indentation fix

* additional tests for coverage

* extending coverage

* nits

* fixing mistake

* better code
2019-04-02 10:07:31 -07:00
Diego Vieira c3eb256139 Update README.md (#5431)
Improve getting started for Docker.
2019-04-02 09:55:06 -07:00
Tom Fox dd1c0fe2e8 Add twitter follow badge (#5464)
* Add twitter follow button

* fix twitter link
2019-04-02 13:06:45 +01:00
greenkeeper[bot] f137f4dd96 Update flow-bin to the latest version 🚀 (#5469)
* chore(package): update flow-bin to version 0.96.0

* chore(package): update lockfile package-lock.json
2019-04-02 03:44:41 +00:00
dependabot[bot] 8df75a730a Bump eslint from 5.15.3 to 5.16.0 (#5468)
Bumps [eslint](https://github.com/eslint/eslint) from 5.15.3 to 5.16.0.
- [Release notes](https://github.com/eslint/eslint/releases)
- [Changelog](https://github.com/eslint/eslint/blob/master/CHANGELOG.md)
- [Commits](https://github.com/eslint/eslint/compare/v5.15.3...v5.16.0)

Signed-off-by: dependabot[bot] <support@dependabot.com>
2019-04-01 10:47:18 +00:00
Arthur Cinader a3c8629fd9 Release notes and version bump for 3.2.3 (#5467) 2019-03-30 18:32:37 -07:00
Diamond Lewis 730f5c5c3d Allow test credentials for Facebook Auth (#5466)
* Allow test credentials for Facebook Auth

* node_env testing
2019-03-30 18:21:41 -05:00
Arthur Cinader edf5b513dc Protected fields fix (#5463)
* fix minor spelling mistake

* Always process userSensitiveFields if they exist

* Cover change to protectedFields
Add start of some more tests for protectedFields
which i need to do to document the feature.

* re-arrange promise deck chairs to not
swallow errors.

* remove noop code

* protect agains the case where options.protectedFields
is set without a _User permission.
2019-03-30 15:38:52 -07:00
Arthur Cinader 11976b8d24 Changelog and package increment for 3.2.1 (#5461)
* changelog and package increment for 3.2.1

* update package-lock to match package.json
2019-03-29 21:27:57 -07:00
Arthur Cinader 03cd314495 Changelog for forthcoming 3.2 release (#5449)
* Changelog for forthcoming 3.2 release

* fix change log comparison

* add parse sdk upgrade to notes
make links

* fix parse sdk version
2019-03-29 17:27:40 -07:00
Arthur Cinader 9d064890d7 Remove an invisible that is causing display issue (#5458)
* Remove an invisible that is causing display issue

* move code transfer to bottom
2019-03-30 00:07:39 +00:00
greenkeeper[bot] 7bf868e751 Update parse to the latest version 🚀 (#5457)
* fix(package): update parse to version 2.3.1

* chore(package): update lockfile package-lock.json
2019-03-29 21:32:13 +00:00
greenkeeper[bot] b2ec5c10ce Update ws to the latest version 🚀 (#5453)
* fix(package): update ws to version 6.2.1

* chore(package): update lockfile package-lock.json
2019-03-27 13:00:32 +00:00
dependabot[bot] 4e07d16148 Bump semver from 5.6.0 to 6.0.0 (#5451)
Bumps [semver](https://github.com/npm/node-semver) from 5.6.0 to 6.0.0.
- [Release notes](https://github.com/npm/node-semver/releases)
- [Changelog](https://github.com/npm/node-semver/blob/master/CHANGELOG.md)
- [Commits](https://github.com/npm/node-semver/compare/v5.6.0...v6.0.0)

Signed-off-by: dependabot[bot] <support@dependabot.com>
2019-03-27 10:48:29 +00:00
dependabot[bot] f2c332ea6a Bump mongodb from 3.2.1 to 3.2.2 (#5446)
Bumps [mongodb](https://github.com/mongodb/node-mongodb-native) from 3.2.1 to 3.2.2.
- [Release notes](https://github.com/mongodb/node-mongodb-native/releases)
- [Changelog](https://github.com/mongodb/node-mongodb-native/blob/master/HISTORY.md)
- [Commits](https://github.com/mongodb/node-mongodb-native/compare/v3.2.1...v3.2.2)

Signed-off-by: dependabot[bot] <support@dependabot.com>
2019-03-25 11:01:31 +00:00
Tom Fox d64a23b2f7 Remove flovilmart's email from the COC (#5438)
* remove flovilmart email

* change email
2019-03-22 12:54:02 -07:00
greenkeeper[bot] 3c39e3232f Update mongodb to the latest version 🚀 (#5441)
* fix(package): update mongodb to version 3.2.1

* chore(package): update lockfile package-lock.json
2019-03-21 20:31:10 +00:00
dependabot[bot] 6376aea320 Bump @babel/preset-env from 7.4.1 to 7.4.2 (#5439)
Bumps [@babel/preset-env](https://github.com/babel/babel) from 7.4.1 to 7.4.2.
- [Release notes](https://github.com/babel/babel/releases)
- [Changelog](https://github.com/babel/babel/blob/master/CHANGELOG.md)
- [Commits](https://github.com/babel/babel/compare/v7.4.1...v7.4.2)

Signed-off-by: dependabot[bot] <support@dependabot.com>
2019-03-21 10:34:08 +00:00
greenkeeper[bot] a27371b6a4 Update babel7 to the latest version 🚀 (#5436)
* chore(package): update @babel/preset-env to version 7.4.1

* chore(package): update lockfile package-lock.json
2019-03-20 02:29:54 +00:00
greenkeeper[bot] 9f2415420b Update babel7 to the latest version 🚀 (#5435)
* chore(package): update @babel/core to version 7.4.0

* chore(package): update @babel/plugin-proposal-object-rest-spread to version 7.4.0

* chore(package): update @babel/plugin-transform-flow-strip-types to version 7.4.0

* chore(package): update @babel/preset-env to version 7.4.0

* chore(package): update lockfile package-lock.json
2019-03-19 22:40:56 +00:00
greenkeeper[bot] 7eddc4c658 Update bcrypt to the latest version 🚀 (#5433)
* chore(package): update bcrypt to version 3.0.5

* chore(package): update lockfile package-lock.json
2019-03-19 13:16:30 +00:00
dependabot[bot] 4cd12e9049 Bump eslint from 5.15.2 to 5.15.3 (#5432)
Bumps [eslint](https://github.com/eslint/eslint) from 5.15.2 to 5.15.3.
- [Release notes](https://github.com/eslint/eslint/releases)
- [Changelog](https://github.com/eslint/eslint/blob/master/CHANGELOG.md)
- [Commits](https://github.com/eslint/eslint/compare/v5.15.2...v5.15.3)

Signed-off-by: dependabot[bot] <support@dependabot.com>
2019-03-19 10:30:37 +00:00
dependabot[bot] adf0633e7b Bump eslint from 5.15.1 to 5.15.2 (#5428)
Bumps [eslint](https://github.com/eslint/eslint) from 5.15.1 to 5.15.2.
- [Release notes](https://github.com/eslint/eslint/releases)
- [Changelog](https://github.com/eslint/eslint/blob/master/CHANGELOG.md)
- [Commits](https://github.com/eslint/eslint/compare/v5.15.1...v5.15.2)

Signed-off-by: dependabot[bot] <support@dependabot.com>
2019-03-18 10:41:57 +00:00
dependabot[bot] 4d9083bdfa Bump flow-bin from 0.94.0 to 0.95.1 (#5427)
Bumps [flow-bin](https://github.com/flowtype/flow-bin) from 0.94.0 to 0.95.1.
- [Release notes](https://github.com/flowtype/flow-bin/releases)
- [Commits](https://github.com/flowtype/flow-bin/compare/v0.94.0...v0.95.1)

Signed-off-by: dependabot[bot] <support@dependabot.com>
2019-03-18 10:32:27 +00:00
greenkeeper[bot] a75f129542 Update pg-promise to the latest version 🚀 (#5424)
* fix(package): update pg-promise to version 8.6.4

* chore(package): update lockfile package-lock.json
2019-03-16 07:05:22 +00:00
Diamond Lewis 2c51011018 Fix Discourse Badge (#5423) 2019-03-15 17:41:35 -07:00
moonion d84566ac28 Ajax password reset (#5332)
* adapted public api route for use with ajax

* Elegant error handling

* Fixed error return

* Public API error flow redone, tests

* Fixed code to pre-build form

* Public API change password return params

* Reverted errors in resetPassword

* Fixed querystring call

* Success test on ajax password reset

* Added few more routes to tests for coverage

* More tests and redone error return slightly

* Updated error text

* Console logs removal, renamed test, added {} to if

* Wrong error sent

* Revert changes

* Revert "Revert changes"

This reverts commit 68ee2c44bf.

* real revert of {}

* nits and test fix

* fix tests

* throw proper error
2019-03-14 16:06:18 -05:00
Guido Ruiz bf033becbd Properly handle return values in beforeSave (#5228)
* added failing test case to CloudCode.spec.js

a possible bug found where beforeSave does not apply changes to request
object if the beforeSave hook ends with 'true' returned

* moddified triggers to return null when beforeSave
also changed test cases to be more descriptive + added extra test case that returns promise in the beforeSave

* address original issue

* Revert "address original issue"

This reverts commit e01c57d1de.

* fix promises and tests

* Add a test to verify that a failed beforeChange hook will
prevent updating the object.
2019-03-14 13:17:29 -05:00
Diamond Lewis 8622e5c378 Prevent Stalebot from closing PRs (#5416) 2019-03-11 19:24:11 -05:00
FatBat bcda96be51 Fixes #5354. (#5396) 2019-03-11 17:13:07 -07:00
greenkeeper[bot] 01e8e5d827 Update pg-promise to the latest version 🚀 (#5415)
* fix(package): update pg-promise to version 8.6.3

* chore(package): update lockfile package-lock.json
2019-03-07 19:05:16 +00:00
greenkeeper[bot] 8d2514571c Update ws to the latest version 🚀 (#5414)
* fix(package): update ws to version 6.2.0

* chore(package): update lockfile package-lock.json
2019-03-06 08:22:18 +00:00
greenkeeper[bot] 3b96ccb03c Update pg-promise to the latest version 🚀 (#5413)
* fix(package): update pg-promise to version 8.6.2

* chore(package): update lockfile package-lock.json
2019-03-05 15:34:06 +00:00
greenkeeper[bot] a452488d61 Update eslint to the latest version 🚀 (#5412)
* chore(package): update eslint to version 5.15.1

* chore(package): update lockfile package-lock.json
2019-03-05 05:40:39 +00:00
greenkeeper[bot] 378f8d28e1 Update pg-promise to the latest version 🚀 (#5410)
* fix(package): update pg-promise to version 8.6.1

* chore(package): update lockfile package-lock.json
2019-03-04 21:02:48 +00:00
greenkeeper[bot] f798a60475 Update pg-promise to the latest version 🚀 (#5408)
* fix(package): update pg-promise to version 8.6.0

* chore(package): update lockfile package-lock.json
2019-03-02 22:23:21 +00:00
greenkeeper[bot] 92eb2656e0 Update eslint to the latest version 🚀 (#5407)
* chore(package): update eslint to version 5.15.0

* chore(package): update lockfile package-lock.json
2019-03-02 07:13:30 +00:00
greenkeeper[bot] 2266976296 Update lint-staged to the latest version 🚀 (#5406)
* chore(package): update lint-staged to version 8.1.5

* chore(package): update lockfile package-lock.json
2019-03-01 17:17:32 +00:00
dependabot[bot] ad506280b2 Bump jasmine-spec-reporter from 4.1.0 to 4.2.1 (#5405)
Bumps [jasmine-spec-reporter](https://github.com/bcaudan/jasmine-spec-reporter) from 4.1.0 to 4.2.1.
- [Release notes](https://github.com/bcaudan/jasmine-spec-reporter/releases)
- [Changelog](https://github.com/bcaudan/jasmine-spec-reporter/blob/master/CHANGELOG.md)
- [Commits](https://github.com/bcaudan/jasmine-spec-reporter/compare/v4.1.0...v4.2.1)

Signed-off-by: dependabot[bot] <support@dependabot.com>
2019-03-01 10:56:16 +00:00
dependabot[bot] 1aecd18a72 Bump uuid from 3.1.0 to 3.3.2 (#5404)
Bumps [uuid](https://github.com/kelektiv/node-uuid) from 3.1.0 to 3.3.2.
- [Release notes](https://github.com/kelektiv/node-uuid/releases)
- [Changelog](https://github.com/kelektiv/node-uuid/blob/master/CHANGELOG.md)
- [Commits](https://github.com/kelektiv/node-uuid/compare/v3.1.0...v3.3.2)

Signed-off-by: dependabot[bot] <support@dependabot.com>
2019-03-01 10:46:26 +00:00
greenkeeper[bot] 56b0786379 Update flow-bin to the latest version 🚀 (#5402)
* chore(package): update flow-bin to version 0.94.0

* chore(package): update lockfile package-lock.json
2019-03-01 03:33:59 +00:00
pateldharad 6eaefd95ae Password requirement custom message (#5399)
* Added validationError(custom message) for Password requirement fail

* Changed validationError from valodationError in README.md
2019-02-28 13:17:31 -08:00
Diamond Lewis 1e7cc7ba00 Revert uws to 10.148.1 (#5398)
https://www.reddit.com/r/node/comments/91kgte/uws_has_been_deprecated/

The release 100.0.1 was a blank project again.

I've pinned all the packages.

This package has been moved to https://github.com/uNetworking/uWebSockets.js

We should consider migrating to it.
2019-02-28 14:12:33 -06:00
dependabot[bot] e0db6eb2ea Bump eslint from 5.14.0 to 5.14.1 (#5377)
Bumps [eslint](https://github.com/eslint/eslint) from 5.14.0 to 5.14.1.
- [Release notes](https://github.com/eslint/eslint/releases)
- [Changelog](https://github.com/eslint/eslint/blob/master/CHANGELOG.md)
- [Commits](https://github.com/eslint/eslint/compare/v5.14.0...v5.14.1)

Signed-off-by: dependabot[bot] <support@dependabot.com>
2019-02-25 22:35:41 +00:00
greenkeeper[bot] 8d284cd967 Update babel7 to the latest version 🚀 (#5395)
* chore(package): update @babel/core to version 7.3.4

* chore(package): update @babel/plugin-proposal-object-rest-spread to version 7.3.4

* chore(package): update @babel/plugin-transform-flow-strip-types to version 7.3.4

* chore(package): update @babel/preset-env to version 7.3.4

* chore(package): update lockfile package-lock.json
2019-02-25 22:13:30 +00:00
awgeorge 86e9351cd0 Reverse update. 2019-02-23 07:28:39 -08:00
awgeorge c5a5f57451 Updates based on review 2019-02-23 07:28:39 -08:00
awgeorge ff33c9939c Use ES6 code 2019-02-23 07:28:39 -08:00
awgeorge 1420df7d7a Update based on @milesrichardson comment https://github.com/parse-community/parse-server/pull/5334#discussion_r252693409 2019-02-23 07:28:39 -08:00
awgeorge ede262c109 Fix linter errors 2019-02-23 07:28:39 -08:00
awgeorge 0dec4931a0 Add filter sensitive fields logic that apply CLPs\nAdd protectedFields CLP\nAdd defaults for protectedFields CLP\nFix tests 2019-02-23 07:28:39 -08:00
awgeorge b343de0c70 Set default protectedFields and remove previous filter logic 2019-02-23 07:28:39 -08:00
awgeorge 95831a5b22 Add new definition and update tests to reflect 2019-02-23 07:28:39 -08:00
awgeorge 493fc99bcb Fix tests to catch errors 2019-02-23 07:28:39 -08:00
awgeorge c69130e0e0 Added negative scenarios for #5301.
- Public read ACL should never expose PII to authenticated and non-authenticated
    - Explicit ACL like custom user Role should be able to read PII
2019-02-23 07:28:39 -08:00
awgeorge 7a6dc3ff29 Add tests to prove a user with valid read ACLs still can't read PII data. 2019-02-23 07:28:39 -08:00
greenkeeper[bot] b246c4ed90 Update parse to the latest version 🚀 (#5385)
* fix(package): update parse to version 2.2.1

* chore(package): update lockfile package-lock.json
2019-02-22 19:00:30 +00:00
dependabot[bot] 9710c755c5 Bump uws from 10.148.1 to 100.0.1 (#5382)
Bumps [uws](https://github.com/uNetworking/uWebSockets.js) from 10.148.1 to 100.0.1.
- [Release notes](https://github.com/uNetworking/uWebSockets.js/releases)
- [Commits](https://github.com/uNetworking/uWebSockets.js/commits)

Signed-off-by: dependabot[bot] <support@dependabot.com>
2019-02-21 10:36:19 +00:00
greenkeeper[bot] 7d0d4cea33 Update pg-promise to the latest version 🚀 (#5381)
* fix(package): update pg-promise to version 8.5.6

* chore(package): update lockfile package-lock.json
2019-02-20 23:20:31 +00:00
dependabot[bot] 95870f4ecc Bump eslint-plugin-flowtype from 3.4.1 to 3.4.2 (#5375)
Bumps [eslint-plugin-flowtype](https://github.com/gajus/eslint-plugin-flowtype) from 3.4.1 to 3.4.2.
- [Release notes](https://github.com/gajus/eslint-plugin-flowtype/releases)
- [Commits](https://github.com/gajus/eslint-plugin-flowtype/compare/v3.4.1...v3.4.2)

Signed-off-by: dependabot[bot] <support@dependabot.com>
2019-02-18 10:55:38 +00:00
dependabot[bot] 852cd6a179 Bump @babel/core from 7.2.2 to 7.3.3 (#5374)
Bumps [@babel/core](https://github.com/babel/babel) from 7.2.2 to 7.3.3.
- [Release notes](https://github.com/babel/babel/releases)
- [Changelog](https://github.com/babel/babel/blob/master/CHANGELOG.md)
- [Commits](https://github.com/babel/babel/compare/v7.2.2...v7.3.3)

Signed-off-by: dependabot[bot] <support@dependabot.com>
2019-02-18 10:45:40 +00:00
dependabot[bot] 2a2130f85a Bump eslint from 5.13.0 to 5.14.0 (#5373)
Bumps [eslint](https://github.com/eslint/eslint) from 5.13.0 to 5.14.0.
- [Release notes](https://github.com/eslint/eslint/releases)
- [Changelog](https://github.com/eslint/eslint/blob/master/CHANGELOG.md)
- [Commits](https://github.com/eslint/eslint/compare/v5.13.0...v5.14.0)

Signed-off-by: dependabot[bot] <support@dependabot.com>
2019-02-18 10:36:03 +00:00
greenkeeper[bot] 416c190993 Update ws to the latest version 🚀 (#5369)
* fix(package): update ws to version 6.1.4

* chore(package): update lockfile package-lock.json
2019-02-16 17:37:09 +00:00
dependabot[bot] 8bfccf1d4e Bump nyc from 13.2.0 to 13.3.0 (#5367)
Bumps [nyc](https://github.com/istanbuljs/nyc) from 13.2.0 to 13.3.0.
- [Release notes](https://github.com/istanbuljs/nyc/releases)
- [Changelog](https://github.com/istanbuljs/nyc/blob/master/CHANGELOG.md)
- [Commits](https://github.com/istanbuljs/nyc/compare/v13.2.0...v13.3.0)

Signed-off-by: dependabot[bot] <support@dependabot.com>
2019-02-15 10:43:04 +00:00
dependabot[bot] 99943ff52b Bump lint-staged from 8.1.3 to 8.1.4 (#5366)
Bumps [lint-staged](https://github.com/okonet/lint-staged) from 8.1.3 to 8.1.4.
- [Release notes](https://github.com/okonet/lint-staged/releases)
- [Commits](https://github.com/okonet/lint-staged/compare/v8.1.3...v8.1.4)

Signed-off-by: dependabot[bot] <support@dependabot.com>
2019-02-15 10:33:23 +00:00
greenkeeper[bot] bc05d71824 Update deepcopy to the latest version 🚀 (#5364)
* fix(package): update deepcopy to version 2.0.0

* chore(package): update lockfile package-lock.json
2019-02-14 17:37:10 +00:00
dependabot[bot] dc76a3ad41 [Security] Bump handlebars from 4.0.12 to 4.1.0 (#5362)
Bumps [handlebars](https://github.com/wycats/handlebars.js) from 4.0.12 to 4.1.0. **This update includes security fixes.**
- [Release notes](https://github.com/wycats/handlebars.js/releases)
- [Changelog](https://github.com/wycats/handlebars.js/blob/v4.1.0/release-notes.md)
- [Commits](https://github.com/wycats/handlebars.js/compare/v4.0.12...v4.1.0)

Signed-off-by: dependabot[bot] <support@dependabot.com>
2019-02-14 04:59:23 +00:00
greenkeeper[bot] 226234e6bc Update parse to the latest version 🚀 (#5361)
* fix(package): update parse to version 2.2.0

* chore(package): update lockfile package-lock.json
2019-02-14 03:03:32 +00:00
greenkeeper[bot] 2be5a784e4 Update flow-bin to the latest version 🚀 (#5360)
* chore(package): update flow-bin to version 0.93.0

* chore(package): update lockfile package-lock.json
2019-02-14 00:18:21 +00:00
greenkeeper[bot] 7bb94c8560 Update follow-redirects to the latest version 🚀 (#5359)
* fix(package): update follow-redirects to version 1.7.0

* chore(package): update lockfile package-lock.json
2019-02-13 23:47:11 +00:00
dependabot[bot] 61d8516eb3 Bump eslint-plugin-flowtype from 3.2.1 to 3.4.1 (#5356)
Bumps [eslint-plugin-flowtype](https://github.com/gajus/eslint-plugin-flowtype) from 3.2.1 to 3.4.1.
- [Release notes](https://github.com/gajus/eslint-plugin-flowtype/releases)
- [Commits](https://github.com/gajus/eslint-plugin-flowtype/compare/v3.2.1...v3.4.1)

Signed-off-by: dependabot[bot] <support@dependabot.com>
2019-02-13 10:34:35 +00:00
greenkeeper[bot] 4d07f0274a Update bcrypt to the latest version 🚀 (#5351)
* chore(package): update bcrypt to version 3.0.4

* chore(package): update lockfile package-lock.json
2019-02-06 23:22:21 +00:00
dependabot[bot] db0ac480af Bump nyc from 13.1.0 to 13.2.0 (#5346)
Bumps [nyc](https://github.com/istanbuljs/nyc) from 13.1.0 to 13.2.0.
- [Release notes](https://github.com/istanbuljs/nyc/releases)
- [Changelog](https://github.com/istanbuljs/nyc/blob/master/CHANGELOG.md)
- [Commits](https://github.com/istanbuljs/nyc/compare/v13.1.0...v13.2.0)

Signed-off-by: dependabot[bot] <support@dependabot.com>
2019-02-05 10:38:54 +00:00
greenkeeper[bot] 5b21a056d8 Update flow-bin to the latest version 🚀 (#5345)
* chore(package): update flow-bin to version 0.92.1

* chore(package): update lockfile package-lock.json
2019-02-05 06:42:54 +00:00
greenkeeper[bot] 8d60726a15 Update babel7 to the latest version 🚀 (#5344)
* chore(package): update @babel/plugin-proposal-object-rest-spread to version 7.3.2

* chore(package): update lockfile package-lock.json
2019-02-05 01:11:33 +00:00
dependabot[bot] 400ff4a750 Bump lint-staged from 8.1.1 to 8.1.3 (#5343)
Bumps [lint-staged](https://github.com/okonet/lint-staged) from 8.1.1 to 8.1.3.
- [Release notes](https://github.com/okonet/lint-staged/releases)
- [Commits](https://github.com/okonet/lint-staged/compare/v8.1.1...v8.1.3)

Signed-off-by: dependabot[bot] <support@dependabot.com>
2019-02-04 11:01:22 +00:00
dependabot[bot] e9f8c616dd Bump prettier from 1.16.3 to 1.16.4 (#5342)
Bumps [prettier](https://github.com/prettier/prettier) from 1.16.3 to 1.16.4.
- [Release notes](https://github.com/prettier/prettier/releases)
- [Changelog](https://github.com/prettier/prettier/blob/master/CHANGELOG.md)
- [Commits](https://github.com/prettier/prettier/compare/1.16.3...1.16.4)

Signed-off-by: dependabot[bot] <support@dependabot.com>
2019-02-04 10:36:59 +00:00
dependabot[bot] 44b26e1993 Bump eslint from 5.12.1 to 5.13.0 (#5341)
Bumps [eslint](https://github.com/eslint/eslint) from 5.12.1 to 5.13.0.
- [Release notes](https://github.com/eslint/eslint/releases)
- [Changelog](https://github.com/eslint/eslint/blob/master/CHANGELOG.md)
- [Commits](https://github.com/eslint/eslint/compare/v5.12.1...v5.13.0)

Signed-off-by: dependabot[bot] <support@dependabot.com>
2019-02-04 10:27:28 +00:00
dependabot[bot] 1973dee59b Bump flow-bin from 0.91.0 to 0.92.0 (#5339)
Bumps [flow-bin](https://github.com/flowtype/flow-bin) from 0.91.0 to 0.92.0.
- [Release notes](https://github.com/flowtype/flow-bin/releases)
- [Commits](https://github.com/flowtype/flow-bin/compare/v0.91.0...v0.92.0)

Signed-off-by: dependabot[bot] <support@dependabot.com>
2019-02-01 09:17:26 -05:00
dependabot[bot] 5471b6a083 Bump deepcopy from 1.0.0 to 1.0.1 (#5340)
Bumps [deepcopy](https://github.com/sasaplus1/deepcopy.js) from 1.0.0 to 1.0.1.
- [Release notes](https://github.com/sasaplus1/deepcopy.js/releases)
- [Changelog](https://github.com/sasaplus1/deepcopy.js/blob/master/HISTORY.md)
- [Commits](https://github.com/sasaplus1/deepcopy.js/compare/1.0.0...1.0.1)

Signed-off-by: dependabot[bot] <support@dependabot.com>
2019-02-01 10:50:14 +00:00
Steve Clay 038d7bd727 postgres: Refuse to build unsafe JSON lists for contains in Postgres (#5337) 2019-01-31 12:23:40 -05:00
dependabot[bot] 7e48b7a1f1 Bump prettier from 1.16.2 to 1.16.3 (#5335)
Bumps [prettier](https://github.com/prettier/prettier) from 1.16.2 to 1.16.3.
- [Release notes](https://github.com/prettier/prettier/releases)
- [Changelog](https://github.com/prettier/prettier/blob/master/CHANGELOG.md)
- [Commits](https://github.com/prettier/prettier/compare/1.16.2...1.16.3)

Signed-off-by: dependabot[bot] <support@dependabot.com>
2019-01-31 10:33:46 +00:00
dependabot[bot] 78eb3df235 Bump prettier from 1.16.1 to 1.16.2 (#5331)
Bumps [prettier](https://github.com/prettier/prettier) from 1.16.1 to 1.16.2.
- [Release notes](https://github.com/prettier/prettier/releases)
- [Changelog](https://github.com/prettier/prettier/blob/master/CHANGELOG.md)
- [Commits](https://github.com/prettier/prettier/compare/1.16.1...1.16.2)

Signed-off-by: dependabot[bot] <support@dependabot.com>
2019-01-30 10:34:22 +00:00
dependabot[bot] bdb497c9a3 Bump lint-staged from 8.1.0 to 8.1.1 (#5329)
Bumps [lint-staged](https://github.com/okonet/lint-staged) from 8.1.0 to 8.1.1.
- [Release notes](https://github.com/okonet/lint-staged/releases)
- [Commits](https://github.com/okonet/lint-staged/compare/v8.1.0...v8.1.1)

Signed-off-by: dependabot[bot] <support@dependabot.com>
2019-01-29 10:41:22 +00:00
dependabot[bot] 22c39ed6c8 Bump @babel/preset-env from 7.2.3 to 7.3.1
Bumps [@babel/preset-env](https://github.com/babel/babel) from 7.2.3 to 7.3.1.
- [Release notes](https://github.com/babel/babel/releases)
- [Changelog](https://github.com/babel/babel/blob/master/CHANGELOG.md)
- [Commits](https://github.com/babel/babel/compare/v7.2.3...v7.3.1)

Signed-off-by: dependabot[bot] <support@dependabot.com>
2019-01-28 00:43:32 +00:00
dependabot[bot] 06550e3830 Bump eslint from 5.12.0 to 5.12.1
Bumps [eslint](https://github.com/eslint/eslint) from 5.12.0 to 5.12.1.
- [Release notes](https://github.com/eslint/eslint/releases)
- [Changelog](https://github.com/eslint/eslint/blob/master/CHANGELOG.md)
- [Commits](https://github.com/eslint/eslint/compare/v5.12.0...v5.12.1)

Signed-off-by: dependabot[bot] <support@dependabot.com>
2019-01-28 00:34:47 +00:00
Florent Vilmart bfec804ee1 chore: bump mongodb to 3.1.13 (#5325) 2019-01-27 19:13:11 -05:00
Darren Black 55d367ec03 Check the correct verbose field when hiding push details (#5324) 2019-01-27 15:35:33 -05:00
Darren Black 6a93806c62 Hides token contents in logStartupOptions if they arrive as a buffer (#5322)
* Hides token contents in logStartupOptions if they arrive as a buffer

* Hides all push details in logStartupOptions unless we're in verbose mode
2019-01-27 13:59:15 -05:00
Marc Smith 1cbe8bd394 Docs Typo (#5317) 2019-01-24 22:21:58 -05:00
Marc Smith 03604b1bc6 Docs Typo #2 (#5318)
Plus added example.com url instead of '....'
2019-01-24 22:20:27 -05:00
dependabot[bot] bb47caad56 Bump ws from 6.1.2 to 6.1.3 (#5316)
Bumps [ws](https://github.com/websockets/ws) from 6.1.2 to 6.1.3.
- [Release notes](https://github.com/websockets/ws/releases)
- [Commits](https://github.com/websockets/ws/compare/6.1.2...6.1.3)

Signed-off-by: dependabot[bot] <support@dependabot.com>
2019-01-24 10:36:22 +00:00
dependabot[bot] f84f245903 Bump prettier from 1.16.0 to 1.16.1 (#5315)
Bumps [prettier](https://github.com/prettier/prettier) from 1.16.0 to 1.16.1.
- [Release notes](https://github.com/prettier/prettier/releases)
- [Changelog](https://github.com/prettier/prettier/blob/master/CHANGELOG.md)
- [Commits](https://github.com/prettier/prettier/compare/1.16.0...1.16.1)

Signed-off-by: dependabot[bot] <support@dependabot.com>
2019-01-23 10:36:36 +00:00
dependabot[bot] 098053da35 Bump prettier from 1.15.3 to 1.16.0
Bumps [prettier](https://github.com/prettier/prettier) from 1.15.3 to 1.16.0.
- [Release notes](https://github.com/prettier/prettier/releases)
- [Changelog](https://github.com/prettier/prettier/blob/master/CHANGELOG.md)
- [Commits](https://github.com/prettier/prettier/compare/1.15.3...1.16.0)

Signed-off-by: dependabot[bot] <support@dependabot.com>
2019-01-22 18:08:56 +00:00
dependabot[bot] 355f9f6b52 Bump @babel/plugin-proposal-object-rest-spread from 7.2.0 to 7.3.1 (#5312)
Bumps [@babel/plugin-proposal-object-rest-spread](https://github.com/babel/babel) from 7.2.0 to 7.3.1.
- [Release notes](https://github.com/babel/babel/releases)
- [Changelog](https://github.com/babel/babel/blob/master/CHANGELOG.md)
- [Commits](https://github.com/babel/babel/compare/v7.2.0...v7.3.1)

Signed-off-by: dependabot[bot] <support@dependabot.com>
2019-01-22 13:07:29 -05:00
dependabot[bot] 3851641b5a Bump flow-bin from 0.83.0 to 0.91.0 (#5302)
* Bump flow-bin from 0.83.0 to 0.91.0

Bumps [flow-bin](https://github.com/flowtype/flow-bin) from 0.83.0 to 0.91.0.
- [Release notes](https://github.com/flowtype/flow-bin/releases)
- [Commits](https://github.com/flowtype/flow-bin/compare/v0.83.0...v0.91.0)

Signed-off-by: dependabot[bot] <support@dependabot.com>

* fix flow issue
2019-01-19 13:08:42 -05:00
dependabot[bot] 30aa1ded1d Bump eslint-plugin-flowtype from 3.2.0 to 3.2.1 (#5284)
Bumps [eslint-plugin-flowtype](https://github.com/gajus/eslint-plugin-flowtype) from 3.2.0 to 3.2.1.
- [Release notes](https://github.com/gajus/eslint-plugin-flowtype/releases)
- [Commits](https://github.com/gajus/eslint-plugin-flowtype/compare/v3.2.0...v3.2.1)

Signed-off-by: dependabot[bot] <support@dependabot.com>
2019-01-19 12:22:53 -05:00
dependabot[bot] be251811eb Bump eslint from 5.11.1 to 5.12.0 (#5282)
Bumps [eslint](https://github.com/eslint/eslint) from 5.11.1 to 5.12.0.
- [Release notes](https://github.com/eslint/eslint/releases)
- [Changelog](https://github.com/eslint/eslint/blob/master/CHANGELOG.md)
- [Commits](https://github.com/eslint/eslint/compare/v5.11.1...v5.12.0)

Signed-off-by: dependabot[bot] <support@dependabot.com>
2019-01-19 12:22:42 -05:00
dependabot[bot] 68913d9a20 Bump supports-color from 6.0.0 to 6.1.0 (#5291)
Bumps [supports-color](https://github.com/chalk/supports-color) from 6.0.0 to 6.1.0.
- [Release notes](https://github.com/chalk/supports-color/releases)
- [Commits](https://github.com/chalk/supports-color/compare/v6.0.0...v6.1.0)

Signed-off-by: dependabot[bot] <support@dependabot.com>
2019-01-19 12:22:23 -05:00
greenkeeper[bot] 6525b39e83 Update mongodb to the latest version 🚀 (#5300)
* fix(package): update mongodb to version 3.2.0

* chore(package): update lockfile package-lock.json
2019-01-16 22:55:46 +00:00
greenkeeper[bot] 3ebdb63268 Update pg-promise to the latest version 🚀 (#5299)
* fix(package): update pg-promise to version 8.5.5

* chore(package): update lockfile package-lock.json
2019-01-16 13:24:38 +00:00
greenkeeper[bot] b0b3cfa39a Update mongodb to the latest version 🚀 (#5298)
* fix(package): update mongodb to version 3.1.11

* chore(package): update lockfile package-lock.json
2019-01-15 22:16:58 +00:00
Florent Vilmart 32de338247 Discourse (#5296)
* Update ISSUE_TEMPLATE.md

* Update ---report-an-issue.md

* Update ---parse-server-3-0-0.md

* Update ---push-notifications.md

* Update README.md

* Create ---getting-help.md
2019-01-15 12:20:57 -05:00
Florent Vilmart 46ac7e7f11 Adds fix for issue affecting update with CLP (#5269)
* Adds fix for issue affecting update with CLP

* Disable single instance
2019-01-04 14:23:27 -05:00
Perceval Archimbaud 9f2fc88f0f Fix issue #5274 on RestQuery.each and relations (#5276)
* Add test on RestQuery.each with relation

* Fix the failing test for RestQuery.each and relations

* Add test for getRolesForUser

* Fix format for comment

* Remove extra fit
2019-01-04 14:22:46 -05:00
greenkeeper[bot] eb877545de Update follow-redirects to the latest version 🚀 (#5277)
* fix(package): update follow-redirects to version 1.6.1

* chore(package): update lockfile package-lock.json
2019-01-03 10:47:19 +00:00
Florent Vilmart cc0d76966c Update lockfile (#5270) 2019-01-01 13:43:50 -05:00
Florent Vilmart 2d7b9923ef ⚡ Release 3.1.3 (#5267)
* ⚡ Release 3.1.3

* Update CHANGELOG.md
2018-12-30 09:45:19 -05:00
dependabot[bot] d11e93c36c Bump eslint from 5.10.0 to 5.11.1 (#5261)
Bumps [eslint](https://github.com/eslint/eslint) from 5.10.0 to 5.11.1.
- [Release notes](https://github.com/eslint/eslint/releases)
- [Changelog](https://github.com/eslint/eslint/blob/master/CHANGELOG.md)
- [Commits](https://github.com/eslint/eslint/compare/v5.10.0...v5.11.1)

Signed-off-by: dependabot[bot] <support@dependabot.com>
2018-12-29 12:35:04 -05:00
dependabot[bot] d04a7b5a77 Bump husky from 1.2.1 to 1.3.1 (#5263)
Bumps [husky](https://github.com/typicode/husky) from 1.2.1 to 1.3.1.
- [Release notes](https://github.com/typicode/husky/releases)
- [Changelog](https://github.com/typicode/husky/blob/master/CHANGELOG.md)
- [Commits](https://github.com/typicode/husky/compare/v1.2.1...v1.3.1)

Signed-off-by: dependabot[bot] <support@dependabot.com>
2018-12-29 12:34:51 -05:00
Diamond Lewis ce7ff2ca44 Add original object to LiveQuery Events (#5265)
* Add original object to LiveQuery Events

* change response original
2018-12-28 17:56:08 -05:00
Benjamin Simonsson de92ce5c49 Fix for count being very slow on large Parse Classes' collections (#5264)
* * Added fix for MongoCollection's count function, so that it uses the much more effecient estimatedDocumentCount if no queries were specified

* * Added missing options when running estimatedDocumentCount for Mongo Collections

* * Fixed issue with checking for zero query for Mongo Collections count
2018-12-28 09:45:36 -05:00
Diamond Lewis 66f594342e Update CHANGELOG.md 2018-12-27 09:06:37 -06:00
greenkeeper[bot] 3a6505a32d Update follow-redirects to the latest version 🚀 (#5260)
* fix(package): update follow-redirects to version 1.6.0

* chore(package): update lockfile package-lock.json
2018-12-25 22:46:16 +00:00
greenkeeper[bot] acf205cd7f Update bcrypt to the latest version 🚀 (#5258)
* chore(package): update bcrypt to version 3.0.3

* chore(package): update lockfile package-lock.json
2018-12-24 04:16:59 +00:00
Diamond Lewis 99698928af Fix for #5207 (#5257) 2018-12-23 19:40:59 -06:00
Florent Vilmart a9aff79cc6 docker: slim docker image to 154Mb (#5248)
* Better dockerfile

* nits

* proper VOLUMES
2018-12-22 11:33:41 -05:00
greenkeeper[bot] df109c0023 Update supports-color to the latest version 🚀 (#5256)
* chore(package): update supports-color to version 6.0.0

* chore(package): update lockfile package-lock.json
2018-12-21 16:02:18 +00:00
greenkeeper[bot] 1d8bcb22bd Update babel7 to the latest version 🚀 (#5255)
* chore(package): update @babel/cli to version 7.2.3

* chore(package): update @babel/plugin-transform-flow-strip-types to version 7.2.3

* chore(package): update @babel/preset-env to version 7.2.3

* chore(package): update lockfile package-lock.json
2018-12-20 13:41:28 +00:00
Diamond Lewis ab32dbc9a2 PG: Fix containedIn query on empty array (#5254)
* PG: Fix containedIn query on empty array

* improve logic
2018-12-19 17:57:55 -06:00
Diamond Lewis 631b1684e2 PG: Updating mixed array test (#5252)
* PG: Updating mixed array test

Currently we can save a mixed array but not update

* build array instead of casting

* fix test

* add recursion
2018-12-18 17:38:05 -06:00
Diamond Lewis 7b3da8b744 PG: Fix updating numeric array (#5251)
* PG: Fix updating numeric array

* lint
2018-12-18 12:11:24 -06:00
Florent Vilmart c762ee4b43 run tests against mongodb 4.0 (#5250)
* run tests against mongodb 4.0

* Adds compatibility shields
2018-12-18 11:41:18 -05:00
Florent Vilmart 315d3bfee8 Use current LTS 10.14.2 for dev and ci (#5249) 2018-12-18 11:21:33 -05:00
dependabot[bot] 980c2cda3a ⬆️ Bump eslint from 5.9.0 to 5.10.0 (#5247)
Bumps [eslint](https://github.com/eslint/eslint) from 5.9.0 to 5.10.0.
- [Release notes](https://github.com/eslint/eslint/releases)
- [Changelog](https://github.com/eslint/eslint/blob/master/CHANGELOG.md)
- [Commits](https://github.com/eslint/eslint/compare/v5.9.0...v5.10.0)

Signed-off-by: dependabot[bot] <support@dependabot.com>
2018-12-17 07:32:44 -05:00
dependabot[bot] 3ea632fa1b Bump winston from 2.4.1 to 2.4.4 (#5203)
Bumps [winston](https://github.com/winstonjs/winston) from 2.4.1 to 2.4.4.
- [Release notes](https://github.com/winstonjs/winston/releases)
- [Changelog](https://github.com/winstonjs/winston/blob/2.4.4/CHANGELOG.md)
- [Commits](https://github.com/winstonjs/winston/compare/2.4.1...2.4.4)

Signed-off-by: dependabot[bot] <support@dependabot.com>
2018-12-15 08:07:44 -05:00
dependabot[bot] f08ca7b9cc Bump follow-redirects from 1.5.9 to 1.5.10 (#5204)
Bumps [follow-redirects](https://github.com/follow-redirects/follow-redirects) from 1.5.9 to 1.5.10.
- [Release notes](https://github.com/follow-redirects/follow-redirects/releases)
- [Commits](https://github.com/follow-redirects/follow-redirects/compare/v1.5.9...v1.5.10)

Signed-off-by: dependabot[bot] <support@dependabot.com>
2018-12-15 08:07:26 -05:00
greenkeeper[bot] 4b8dc9409f Update babel7 to the latest version 🚀 (#5243)
* chore(package): update @babel/core to version 7.2.2

* chore(package): update @babel/plugin-transform-flow-strip-types to version 7.2.0

* chore(package): update @babel/preset-env to version 7.2.0

* chore(package): update lockfile package-lock.json
2018-12-15 11:42:07 +00:00
Diamond Lewis d478e001b5 PG: Support multiple global config (#5242)
* PG: Support Multiple Configs

* rename test

* refactor
2018-12-14 17:39:07 -06:00
Diamond Lewis 8c419ec52a Update CONTRIBUTING.md (#5241) 2018-12-14 10:46:37 -05:00
dependabot[bot] 9470dfc88a Bump @babel/plugin-proposal-object-rest-spread from 7.0.0 to 7.2.0 (#5221)
Bumps [@babel/plugin-proposal-object-rest-spread](https://github.com/babel/babel) from 7.0.0 to 7.2.0.
- [Release notes](https://github.com/babel/babel/releases)
- [Changelog](https://github.com/babel/babel/blob/master/CHANGELOG.md)
- [Commits](https://github.com/babel/babel/compare/v7.0.0...v7.2.0)

Signed-off-by: dependabot[bot] <support@dependabot.com>
2018-12-14 10:46:10 -05:00
dependabot[bot] 13626290c0 Bump express from 4.16.2 to 4.16.4 (#5202)
Bumps [express](https://github.com/expressjs/express) from 4.16.2 to 4.16.4.
- [Release notes](https://github.com/expressjs/express/releases)
- [Changelog](https://github.com/expressjs/express/blob/master/History.md)
- [Commits](https://github.com/expressjs/express/compare/4.16.2...4.16.4)

Signed-off-by: dependabot[bot] <support@dependabot.com>
2018-12-14 10:44:27 -05:00
dependabot[bot] cbd31d513e Bump nyc from 12.0.2 to 13.1.0 (#5206)
Bumps [nyc](https://github.com/istanbuljs/nyc) from 12.0.2 to 13.1.0.
- [Release notes](https://github.com/istanbuljs/nyc/releases)
- [Changelog](https://github.com/istanbuljs/nyc/blob/master/CHANGELOG.md)
- [Commits](https://github.com/istanbuljs/nyc/compare/v12.0.2...v13.1.0)

Signed-off-by: dependabot[bot] <support@dependabot.com>
2018-12-14 10:44:11 -05:00
dependabot[bot] 3e414a8c60 Bump lint-staged from 7.3.0 to 8.1.0 (#5211)
Bumps [lint-staged](https://github.com/okonet/lint-staged) from 7.3.0 to 8.1.0.
- [Release notes](https://github.com/okonet/lint-staged/releases)
- [Commits](https://github.com/okonet/lint-staged/compare/v7.3.0...v8.1.0)

Signed-off-by: dependabot[bot] <support@dependabot.com>
2018-12-14 10:43:55 -05:00
dependabot[bot] 38482cf0fe Bump lru-cache from 5.0.0 to 5.1.1 (#5213)
Bumps [lru-cache](https://github.com/isaacs/node-lru-cache) from 5.0.0 to 5.1.1.
- [Release notes](https://github.com/isaacs/node-lru-cache/releases)
- [Commits](https://github.com/isaacs/node-lru-cache/compare/v5.0.0...v5.1.1)

Signed-off-by: dependabot[bot] <support@dependabot.com>
2018-12-14 10:43:34 -05:00
dependabot[bot] 8c044ab85b Bump @babel/cli from 7.1.5 to 7.2.0 (#5222)
Bumps [@babel/cli](https://github.com/babel/babel) from 7.1.5 to 7.2.0.
- [Release notes](https://github.com/babel/babel/releases)
- [Changelog](https://github.com/babel/babel/blob/master/CHANGELOG.md)
- [Commits](https://github.com/babel/babel/compare/v7.1.5...v7.2.0)

Signed-off-by: dependabot[bot] <support@dependabot.com>
2018-12-14 10:43:16 -05:00
dependabot[bot] 208f92160d Bump husky from 1.0.0-rc.15 to 1.2.1 (#5237)
Bumps [husky](https://github.com/typicode/husky) from 1.0.0-rc.15 to 1.2.1.
- [Release notes](https://github.com/typicode/husky/releases)
- [Changelog](https://github.com/typicode/husky/blob/master/CHANGELOG.md)
- [Commits](https://github.com/typicode/husky/compare/v1.0.0-rc.15...v1.2.1)

Signed-off-by: dependabot[bot] <support@dependabot.com>
2018-12-14 10:42:07 -05:00
greenkeeper[bot] e28770e744 Update pg-promise to the latest version 🚀 (#5234)
* fix(package): update pg-promise to version 8.5.4

* chore(package): update lockfile package-lock.json
2018-12-14 01:29:47 +00:00
greenkeeper[bot] a4b592a355 Update pg-promise to the latest version 🚀 (#5217)
* fix(package): update pg-promise to version 8.5.3

* chore(package): update lockfile package-lock.json
2018-11-30 04:54:59 +00:00
greenkeeper[bot] f0626508d3 Update prettier to the latest version 🚀 (#5216)
* chore(package): update prettier to version 1.15.3

* chore(package): update lockfile package-lock.json
2018-11-30 03:28:53 +00:00
dependabot[bot] bc12b1496a Bump babel-eslint from 10.0.0 to 10.0.1 (#5200)
Bumps [babel-eslint](https://github.com/babel/babel-eslint) from 10.0.0 to 10.0.1.
- [Release notes](https://github.com/babel/babel-eslint/releases)
- [Commits](https://github.com/babel/babel-eslint/compare/v10.0.0...v10.0.1)

Signed-off-by: dependabot[bot] <support@dependabot.com>
2018-11-27 21:01:40 -05:00
dependabot[bot] 80a9b70253 Bump eslint-plugin-flowtype from 3.0.0 to 3.2.0 (#5197)
Bumps [eslint-plugin-flowtype](https://github.com/gajus/eslint-plugin-flowtype) from 3.0.0 to 3.2.0.
- [Release notes](https://github.com/gajus/eslint-plugin-flowtype/releases)
- [Commits](https://github.com/gajus/eslint-plugin-flowtype/compare/v3.0.0...v3.2.0)

Signed-off-by: dependabot[bot] <support@dependabot.com>
2018-11-27 20:58:37 -05:00
dependabot[bot] 5d9d7601a9 Bump eslint from 5.6.0 to 5.9.0 (#5199)
Bumps [eslint](https://github.com/eslint/eslint) from 5.6.0 to 5.9.0.
- [Release notes](https://github.com/eslint/eslint/releases)
- [Changelog](https://github.com/eslint/eslint/blob/master/CHANGELOG.md)
- [Commits](https://github.com/eslint/eslint/compare/v5.6.0...v5.9.0)

Signed-off-by: dependabot[bot] <support@dependabot.com>
2018-11-27 20:58:21 -05:00
Florent Vilmart 021a12f648 ⚡ Release 3.1.2 (#5193)
- Removes dev script
- Removes nodemon and problematic dependenceis
- addressed event-stream security debacle
2018-11-26 20:57:18 -05:00
greenkeeper[bot] 6cb00d513b Update mime to the latest version 🚀 (#5194)
* fix(package): update mime to version 2.4.0

* chore(package): update lockfile package-lock.json
2018-11-27 01:39:08 +00:00
greenkeeper[bot] 7984bc2ae3 Update lru-cache to the latest version 🚀 (#5189)
* fix(package): update lru-cache to version 5.0.0

* chore(package): update lockfile package-lock.json
2018-11-21 02:17:19 +00:00
greenkeeper[bot] 6659b11962 Update lru-cache to the latest version 🚀 (#5188)
* fix(package): update lru-cache to version 4.1.4

* chore(package): update lockfile package-lock.json
2018-11-21 00:34:02 +00:00
greenkeeper[bot] 284d04a211 Update ws to the latest version 🚀 (#5180)
* fix(package): update ws to version 6.1.2

* chore(package): update lockfile package-lock.json
2018-11-17 20:29:42 +00:00
greenkeeper[bot] e1de2763c3 Update ws to the latest version 🚀 (#5179)
* fix(package): update ws to version 6.1.1

* chore(package): update lockfile package-lock.json
2018-11-17 07:53:16 +00:00
greenkeeper[bot] e04fd9e7fe Update mongodb to the latest version 🚀 (#5178)
* fix(package): update mongodb to version 3.1.10

* chore(package): update lockfile package-lock.json
2018-11-16 22:16:28 +00:00
greenkeeper[bot] 463e52b063 Update babel7 to the latest version 🚀 (#5173)
* chore(package): update @babel/core to version 7.1.6

* chore(package): update @babel/plugin-transform-flow-strip-types to version 7.1.6

* chore(package): update @babel/preset-env to version 7.1.6

* chore(package): update lockfile package-lock.json
2018-11-14 16:41:59 +00:00
greenkeeper[bot] 29bf0132f9 Update prettier to the latest version 🚀 (#5169)
* chore(package): update prettier to version 1.15.2

* chore(package): update lockfile package-lock.json
2018-11-10 18:12:58 +00:00
greenkeeper[bot] ec06b70b8a Update pg-promise to the latest version 🚀 (#5166)
* fix(package): update pg-promise to version 8.5.2

* chore(package): update lockfile package-lock.json
2018-11-08 06:28:19 +00:00
greenkeeper[bot] 42f946dbc6 Update prettier to the latest version 🚀 (#5165)
* chore(package): update prettier to version 1.15.1

* chore(package): update lockfile package-lock.json
2018-11-07 04:57:29 +00:00
greenkeeper[bot] 3254e425e2 Update prettier to the latest version 🚀 (#5164)
* chore(package): update prettier to version 1.15.0

* chore(package): update lockfile package-lock.json
2018-11-07 03:45:48 +00:00
greenkeeper[bot] 18d9811eaa Update babel7 to the latest version 🚀 (#5163)
* chore(package): update @babel/cli to version 7.1.5

* chore(package): update @babel/core to version 7.1.5

* chore(package): update @babel/preset-env to version 7.1.5

* chore(package): update lockfile package-lock.json
2018-11-07 00:45:14 +00:00
greenkeeper[bot] 216579bb01 Update mongodb to the latest version 🚀 (#5162)
* fix(package): update mongodb to version 3.1.9

* chore(package): update lockfile package-lock.json
2018-11-06 13:49:03 +00:00
Antoine Cormouls d356ef5633 Release 3.1.1 🚀 (#5149)
* Release 3.1.1

* Requested Changes
2018-10-27 11:34:20 -04:00
Diamond Lewis daab3781a2 Support Distinct for special fields (#5144)
* Support Distinct for special fields

* update changelog
2018-10-26 10:21:42 -05:00
Georges Jamous 961abda4eb "Object not found." instead of "Insufficient auth." when using master key (#5133)
* add additional isMaster check

* adding some tests

* nits

* covering all basis
2018-10-22 19:33:43 -04:00
Antoine Cormouls de79b70cbc Ensure all roles are properly loaded #5131 (#5132)
* Fix Limitation Role #5131

Allow to manage Live Query with User that have more than 100 Parse.Roles

* Clean Up

* Add Custom Config Support and Test

* Fix Auth Test

* Switch to Async Function

* Fix restWhere

* Fix Test

* Clean Final Commit

* Lint Fix

* Need to Fix Test Callback

* Fixes broken test

* Restore find() method in spy

* adds restquery-each

* small nit

* adds changelog
2018-10-20 16:45:23 -04:00
greenkeeper[bot] aa9580e59c Update pg-promise to the latest version 🚀 (#5134)
* fix(package): update pg-promise to version 8.5.1

* chore(package): update lockfile package-lock.json
2018-10-19 19:24:35 +00:00
Arthur Cinader c692f075d1 ⚡ Release 3.1.0 (#5127) 2018-10-18 08:19:07 -04:00
Florent Vilmart 4b7037ac9a Fix intense CPU usage when sessionToken is invalid in liveQuery (#5126)
* Ensure we bail out early when auth or userId are not provided (sessionToken fetch is invalid)

* Adds changelog

* better handling of session token errors and client tokens
2018-10-18 07:21:31 -04:00
greenkeeper[bot] 318a784e20 Update bcrypt to the latest version 🚀 (#5128)
* chore(package): update bcrypt to version 3.0.2

* chore(package): update lockfile package-lock.json
2018-10-18 03:21:09 +00:00
Florent Vilmart 7c81290252 Live query CLP (#4387)
* Auth module refactoring in order to be reusable

* Ensure cache controller is properly forwarded from helpers

* Nits

* Adds support for static validation

* Adds support for CLP in Live query (no support for roles yet)

* Adds e2e test to validate liveQuery hooks is properly called

* Adds tests over LiveQueryController to ensure data is correctly transmitted

* nits

* Fixes for flow types

* Removes usage of Parse.Promise

* Use the Auth module for authentication and caches

* Cleaner implementation of getting auth

* Adds authCache that stores auth promises

* Proper testing of the caching

* nits
2018-10-17 17:53:49 -04:00
Arthur Cinader 17bd5c3adb Add changelog for two already merged pr's 2018-10-16 13:19:20 -07:00
greenkeeper[bot] 07220b351c Update eslint-plugin-flowtype to the latest version 🚀 (#5121)
* chore(package): update eslint-plugin-flowtype to version 3.0.0

* chore(package): update lockfile package-lock.json
2018-10-13 23:17:09 +00:00
greenkeeper[bot] 0685a0e7fb Update flow-bin to the latest version 🚀 (#5118)
* chore(package): update flow-bin to version 0.83.0

* chore(package): update lockfile package-lock.json
2018-10-11 18:00:17 +00:00
Arthur Cinader 5fba636517 fix typo 2018-10-11 09:09:56 -07:00
Arthur Cinader f9e108c4ee nit 2018-10-11 09:09:56 -07:00
Arthur Cinader a0de2bcb6b Move logic out of User and Classes controllers
into RestWriter
2018-10-11 09:09:56 -07:00
Arthur Cinader b3b4461fe2 pr comments:
consolidate write operations
also tweak test text
2018-10-11 09:09:56 -07:00
Arthur Cinader 6ebce1832b Expire password reset tokens if user's email changes. 2018-10-11 09:09:56 -07:00
Arthur Cinader 152ff41cf8 fix the expectation on the failing test. 2018-10-11 09:09:56 -07:00
Arthur Cinader 34b51f7289 Add failing test to show
that changing a user's email
does not delete their perishable token.
2018-10-11 09:09:56 -07:00
Arthur Cinader 317682d7f6 Typos 2018-10-11 09:09:56 -07:00
greenkeeper[bot] 8dff708613 Update semver to the latest version 🚀 (#5117)
* fix(package): update semver to version 5.6.0

* chore(package): update lockfile package-lock.json
2018-10-11 00:14:26 +00:00
greenkeeper[bot] 635f54bd08 Update mongodb to the latest version 🚀 (#5115)
* fix(package): update mongodb to version 3.1.8

* chore(package): update lockfile package-lock.json
2018-10-10 22:49:52 +00:00
greenkeeper[bot] 2ce3c9c378 Update mongodb to the latest version 🚀 (#5112)
* fix(package): update mongodb to version 3.1.7

* chore(package): update lockfile package-lock.json
2018-10-09 17:55:58 -04:00
greenkeeper[bot] 645ddaf661 Update commander to the latest version 🚀 (#5108)
* fix(package): update commander to version 2.19.0

* chore(package): update lockfile package-lock.json
2018-10-09 17:55:46 -04:00
greenkeeper[bot] 5373cb7998 Update follow-redirects to the latest version 🚀 (#5111)
* fix(package): update follow-redirects to version 1.5.9

* chore(package): update lockfile package-lock.json
2018-10-09 16:24:35 +00:00
greenkeeper[bot] 2f0e5819f7 Update pg-promise to the latest version 🚀 (#5110)
* fix(package): update pg-promise to version 8.5.0

* chore(package): update lockfile package-lock.json
2018-10-09 13:49:39 +00:00
Florent Vilmart f1bc55bf89 Reduces number of calls to injectDefaultSchema (#5107) 2018-10-08 14:16:29 -04:00
Arthur Cinader 7fe4030453 Return success on sendPasswordResetEmail even if email not found. 2018-10-05 10:03:41 -07:00
greenkeeper[bot] 7a01fa0a67 Update ws to the latest version 🚀 (#5101)
* fix(package): update ws to version 6.1.0

* chore(package): update lockfile package-lock.json
2018-10-05 07:37:43 +00:00
Paulo Reis 937bdf9058 Fix bugs in dev commands and organize the dev script (#5099) 2018-10-03 07:33:53 -04:00
greenkeeper[bot] 22fe390be6 Update flow-bin to the latest version 🚀 (#5095)
* chore(package): update flow-bin to version 0.82.0

* chore(package): update lockfile package-lock.json
2018-10-01 21:39:55 +00:00
Florent Vilmart 135a993d32 Greenkeeper/mongodb runner 4.3.2 (#5094)
* chore(package): update mongodb-runner to version 4.3.2

Closes #5087

* chore(package): update lockfile package-lock.json
2018-09-30 20:00:51 -04:00
greenkeeper[bot] 827e008497 Update babel7 to the latest version 🚀 (#5090)
* chore(package): update @babel/cli to version 7.1.2

* chore(package): update @babel/core to version 7.1.2

* chore(package): update lockfile package-lock.json
2018-09-28 23:44:55 +00:00
greenkeeper[bot] 9f56b537fb Update babel7 to the latest version 🚀 (#5089)
* chore(package): update @babel/cli to version 7.1.1

* chore(package): update @babel/core to version 7.1.1

* chore(package): update lockfile package-lock.json
2018-09-28 21:12:52 +00:00
Ben Briggs 45ff23bde2 postgres: Ensure that setting a value to undefined is a no-op on writes. (#5065) (#5069) 2018-09-26 16:42:06 -04:00
greenkeeper[bot] 4b5fb84678 Update mongodb-runner to the latest version 🚀 (#5086)
* chore(package): update mongodb-runner to version 4.2.0

* chore(package): update lockfile package-lock.json
2018-09-26 20:37:10 +00:00
greenkeeper[bot] 1c024c1ba2 Update babel-eslint to the latest version 🚀 (#5082)
* chore(package): update babel-eslint to version 10.0.0

* chore(package): update lockfile package-lock.json
2018-09-25 23:23:24 +00:00
greenkeeper[bot] db90157191 Update @parse/push-adapter to the latest version 🚀 (#5078)
* fix(package): update @parse/push-adapter to version 3.0.0

* chore(package): update lockfile package-lock.json
2018-09-25 02:56:10 +00:00
Florent Vilmart 045d941aef Remove request and request-promise from dev dependencies (#5077)
* removes from emailverificationtoken spec

* updates winston

* Updates ValidationAndPasswordsReset

* Use local request in schemas

* Removes request in rest.spec

* Removes request from PushRouter0

* removes request from public API

* removes request from index.spec

* Removes request form parse.push spec

* removes request from ParseInstallation spec

* Removes from ParseHooks

* removes request from ParseGlobalConfig.spec

* Removes request from ParseAPI.spec.js

* removes request from LogsRouter

* removes in features

* Filters undefined headers instead of crashing

* Removes request from ParseUser spec

* Removes usage of request in ParseFile.spec.js

* Removes request from AuthAdapters.js

* removes request-promise from ParseGeoPoint.spec

* Removes request-promise from ParseQuery spec

* remove request-promise from UserPII

* removes request-promise from EnableExpressErrorHandler

* Updates RevocableSessionUpgrade spec

* Update RestQuery

* Removes read preferenceOptionM

* ensure we forward auth from URL

* use request in CloudCode.spec.js

* Removes request-promise from JobSchedule.spec

* Removes rp from VerifyUserPassword.spec.js

* Removes rp from PasswordPolicy spec

* Removes rp from ParsePolygon spec

* Removes rp from fullTextSearch spec

* Removes rp from PArseQuery.Aggregate

* Ensure we properly forward errors

* Removes request and request-promise
2018-09-24 17:07:51 -04:00
Florent Vilmart 93a0017b25 remove runtime dependency on request (#5076) 2018-09-23 12:31:08 -04:00
greenkeeper[bot] 4dc4a3a56d Update bcrypt to the latest version 🚀 (#5070)
* chore(package): update bcrypt to version 3.0.1

* chore(package): update lockfile package-lock.json
2018-09-21 13:17:00 +00:00
Stephan Hoogland 979af97b01 Fixed index link to upgrading to parse 3.0.0 (#5060) 2018-09-20 18:07:10 -04:00
greenkeeper[bot] f928ad702f Update prettier to the latest version 🚀 (#5066)
* chore(package): update prettier to version 1.14.3

* chore(package): update lockfile package-lock.json
2018-09-19 15:28:31 +00:00
Eric 91f121eb62 Allow throw native Error in beforeSave hook (#5064) 2018-09-19 07:02:43 -04:00
Florent Vilmart 5177d5d0af Update and rename .stale.yml to stale.yml 2018-09-17 20:29:31 -04:00
greenkeeper[bot] 2613c353d1 Update babel7 to the latest version 🚀 (#5061)
* chore(package): update @babel/cli to version 7.1.0

* chore(package): update @babel/core to version 7.1.0

* chore(package): update @babel/preset-env to version 7.1.0

* chore(package): update lockfile package-lock.json
2018-09-18 00:23:09 +00:00
greenkeeper[bot] 1a6e19ae94 Update mongodb to the latest version 🚀 (#5056)
* fix(package): update mongodb to version 3.1.6

* chore(package): update lockfile package-lock.json
2018-09-15 02:24:12 +00:00
greenkeeper[bot] 0b37af5e13 Update flow-bin to the latest version 🚀 (#5055)
* chore(package): update flow-bin to version 0.81.0

* chore(package): update lockfile package-lock.json
2018-09-14 23:40:19 +00:00
greenkeeper[bot] 037db69f6a Update mongodb to the latest version 🚀 (#5054)
* fix(package): update mongodb to version 3.1.5

* chore(package): update lockfile package-lock.json
2018-09-14 20:11:32 +00:00
greenkeeper[bot] d18d9c5964 Update lodash to the latest version 🚀 (#5050)
* fix(package): update lodash to version 4.17.11

* chore(package): update lockfile package-lock.json
2018-09-12 21:04:56 +00:00
greenkeeper[bot] 826d65770a Update babel7 to the latest version 🚀 (#5049)
* chore(package): update @babel/core to version 7.0.1

* chore(package): update lockfile
2018-09-12 03:14:00 +00:00
Florent Vilmart 576779b69b Updates JS SDK to 2.1.0 (#5043) 2018-09-08 20:05:32 -04:00
greenkeeper[bot] a3d6d2498c Update flow-bin to the latest version 🚀 (#5021)
* chore(package): update flow-bin to version 0.80.0

* chore(package): update lockfile

https://npm.im/greenkeeper-lockfile
2018-09-08 19:34:35 -04:00
greenkeeper[bot] ec2993918c Update commander to the latest version 🚀 (#5035)
* fix(package): update commander to version 2.18.0

* chore(package): update lockfile

https://npm.im/greenkeeper-lockfile
2018-09-08 23:14:08 +00:00
Florent Vilmart cfa22d0a62 Use spies for CloudCode logger tests (#5042) 2018-09-08 18:46:26 -04:00
Florent Vilmart 617e8405ff Updates definition build script for babel 7.0 (#5024)
* Updates definition build script for babel 7.0

* run eslint on staged files
2018-09-08 14:12:23 -04:00
llino fefecfeb9a Update 3.0.0.md (#5038)
In example, the parameter is called request but in the function it was calling req
2018-09-08 08:20:34 -04:00
Deokhyun Ko 462f5eb54b fix unexpected 'delete' trigger issue on LiveQuery (#5031)
* fix unexpected 'delete' trigger on LiveQuery when response is an error in cloud function beforeDelete

* tested

* Rebuild CI

* Retest due to local machine testing is passed

* Travis testing failed randomly on Cloud Code Logger. I will retry

* retry

* retry
2018-09-06 07:15:36 -04:00
Florent Vilmart a42101531a Silences warnings from mongodb client (#5025)
* Silences warnings from mongodb client

* Update count, delete and finds to recommended implementations

* With new parser, readPref will be null by default

* Update flaky specs wih async/await style

* Adds gridstore adapter spec

* Use GridFSBucketStorage adapter
2018-09-04 16:15:09 -04:00
Florent Vilmart d83a0b6808 Use Prettier JS (#5017)
* Adds prettier

* Run lint before tests
2018-09-01 13:58:06 -04:00
greenkeeper[bot] 189cd259ee Update jsdoc-babel to the latest version 🚀 (#5022)
* chore(package): update jsdoc-babel to version 0.5.0

* chore(package): update lockfile

https://npm.im/greenkeeper-lockfile

* Use babel 7.0

* Proper configuration for jsdoc
2018-09-01 17:02:31 +00:00
orette c7357ed109 Use the correct function when validating google auth tokens (#5018)
* Use the correct function when validating google auth tokens

httpsRequest.request expects the param postData and has no default value
or validation to check if it is missing before using it. As a result, an
error `TypeError: First argument must be a string or Buffer` is
thrown when an attempt is made to authenticate with Google.

A quick check on the LinkedIn, FB, and twitter authentication adapters
shows they are using httpsRequest.get for their validation calls.

* Correct google auth adapter tests
2018-08-30 20:17:46 -04:00
greenkeeper[bot] 8c0a4430e0 Update nodemon to the latest version 🚀 (#5012)
* chore(package): update nodemon to version 1.18.4

* chore(package): update lockfile

https://npm.im/greenkeeper-lockfile
2018-08-29 18:32:39 +00:00
greenkeeper[bot] 36eddbafad Update mongodb-runner to the latest version 🚀 (#5010)
* chore(package): update mongodb-runner to version 4.1.0

* chore(package): update lockfile

https://npm.im/greenkeeper-lockfile
2018-08-29 01:11:37 +00:00
greenkeeper[bot] bd2342ebfa Update babel-eslint to the latest version 🚀 (#5009)
* chore(package): update babel-eslint to version 9.0.0

* chore(package): update lockfile

https://npm.im/greenkeeper-lockfile
2018-08-28 03:22:17 +00:00
greenkeeper[bot] 5236039b1e Update parse to the latest version 🚀 (#5008)
* fix(package): update parse to version 2.0.2

* chore(package): update lockfile

https://npm.im/greenkeeper-lockfile
2018-08-27 15:51:56 +00:00
greenkeeper[bot] b51f508d59 Update mongodb to the latest version 🚀 (#5004)
* fix(package): update mongodb to version 3.1.4

* chore(package): update lockfile

https://npm.im/greenkeeper-lockfile
2018-08-25 16:58:45 +00:00
John Carlson 61720181b5 Update Gomix to Glitch (#4999) 2018-08-21 13:28:49 +00:00
Florent Vilmart 786ea115d7 Update ---push-notifications.md (#4994)
* Update ---push-notifications.md

* Update ---push-notifications.md
2018-08-19 10:43:02 -04:00
Florent Vilmart 40d2cab159 Update issue templates (#4990)
* Update issue templates

* Update ---report-an-issue.md
2018-08-18 17:15:01 -04:00
Florent Vilmart 07164f55d3 Ensure the docs are properly generated (#4983) 2018-08-18 20:09:13 +00:00
greenkeeper[bot] da63feed99 Update pg-promise to the latest version 🚀 (#4989)
* fix(package): update pg-promise to version 8.4.6

* chore(package): update lockfile

https://npm.im/greenkeeper-lockfile
2018-08-18 16:49:48 +00:00
greenkeeper[bot] ddb0fd81c7 Merged by Peril
* fix(package): update semver to version 5.5.1

* chore(package): update lockfile

https://npm.im/greenkeeper-lockfile
2018-08-18 04:07:58 +00:00
greenkeeper[bot] 1b1928fd64 Merged by Peril
* chore(package): update flow-bin to version 0.79.1

* chore(package): update lockfile

https://npm.im/greenkeeper-lockfile
2018-08-18 03:39:10 +00:00
eddyLazar 41acaa0570 fixed vkontakte validateAuthData (#4977)
* fixed vkontakte validateAuthData

* updated changelog
2018-08-17 15:38:07 -04:00
Florent Vilmart f771160147 ⚡ Release 3.0.0 (#4982) 2018-08-17 13:03:02 -04:00
Florent Vilmart e880941cef Update ISSUE_TEMPLATE.md (#4981) 2018-08-17 12:08:36 -04:00
Florent Vilmart 16607cb12a Update .stale.yml 2018-08-17 11:51:44 -04:00
Florent Vilmart d6cd3da61d Create .stale.yml 2018-08-17 11:26:48 -04:00
greenkeeper[bot] bf1f6024d6 Update flow-bin to the latest version 🚀 (#4978)
* chore(package): update flow-bin to version 0.79.0

* chore(package): update lockfile

https://npm.im/greenkeeper-lockfile
2018-08-17 11:24:51 -04:00
Florent Vilmart 9deaca3c07 Delete Bug_report.md 2018-08-17 11:21:52 -04:00
greenkeeper[bot] d25d1f65dc Update deep-diff to the latest version 🚀 (#4974)
* chore(package): update deep-diff to version 1.0.2

* chore(package): update lockfile

https://npm.im/greenkeeper-lockfile
2018-08-16 11:26:02 -04:00
Florent Vilmart b740db335b Allows undefined values to pass from mongo to database controler (#4973)
* Allows undefined values to pass from mongo to database controler

* Adds changelog
2018-08-16 11:24:33 -04:00
Florent Vilmart 942b9b5b1a Adds documentations for adapters and main constructors (#4951)
* wip

* Adds documentations for adapters, and constructors

* nits

* nit
2018-08-15 10:47:21 -04:00
Florent Vilmart 0571675fcd version 3.0.0 API Docs (#4943)
* Adds documentation and docs generation upon merge

* nits
2018-08-15 10:47:21 -04:00
Florent Vilmart 457d51a972 Adds context object in Cloud Code hooks (#4939)
* wip

* Refactors triggers a bit

- Adds testing for hooks and context

* comment nit

* nits
2018-08-15 10:47:21 -04:00
Florent Vilmart 488b2ff231 Migrate to new cloud code interfaces
removes job status object, moves messasge method on req object

Adds 3.0.0 migration guide

Fixes nits about 3.0.0 documentation

Adds update guide to README
2018-08-15 10:47:21 -04:00
Arthur Cinader e46830a57f sync package-lock to package. (#4970) 2018-08-15 10:28:33 -04:00
Florent Vilmart bf9fdb41ed Ensure the ACL is always part of the payload when using select (#4967)
* Ensure the ACL is always part of the payload when using select

* Update changelog

* fix for PG
2018-08-15 09:48:32 -04:00
Arthur Cinader af45b4df74 add log for mailgun adapter update 2018-08-15 09:07:10 -04:00
Arthur Cinader 74218cc36a nit 2018-08-15 09:07:10 -04:00
greenkeeper[bot] 48168feb55 Update request to the latest version 🚀 (#4949)
* fix(package): update request to version 2.88.0

* chore(package): update lockfile

https://npm.im/greenkeeper-lockfile
2018-08-15 09:06:36 -04:00
Florent Vilmart 5e15cc9d4c * fix(package): update mongodb to version 3.1.3 (#4969)
* fix(package): update mongodb to version 3.1.3

Closes #4962

* chore(package): update lockfile

https://npm.im/greenkeeper-lockfile
2018-08-15 09:05:57 -04:00
Arthur Cinader 2765f3390d use updated mailgun adapter 2018-08-14 15:33:48 -04:00
Diamond Lewis 4802b1caec Add pipeline key to Aggregate (#4959)
* Add pipeline key to Aggregate

* clean up

* unit tests
2018-08-12 20:05:08 -05:00
Florent Vilmart 9e36a3cc85 ⚡ Release 2.8.4 2018-08-12 19:06:16 -04:00
Florent Vilmart 8ba8a3393d Removes second log when responding with a regular error 2018-08-12 19:04:48 -04:00
Florent Vilmart b9673da07b Refactor all auth adapters to reduce duplications (#4954)
* Refactor all auth adapters to reduce duplications

* Adds mocking and proper testing for all auth adapters

* Proper testing of the google auth adapter

* noit
2018-08-12 11:05:28 -04:00
Diamond Lewis f1b008388c Support include('*') Query (#4947) 2018-08-10 12:51:32 -05:00
greenkeeper[bot] 35da17b32a Update parse to the latest version 🚀 (#4942)
* fix(package): update parse to version 2.0.1

* chore(package): update lockfile

https://npm.im/greenkeeper-lockfile
2018-08-09 16:20:28 -04:00
Florent Vilmart 2ae603574c Auth module refactoring in order to be reusable (#4940)
* Auth module refactoring in order to be reusable

* Ensure cache controller is properly forwarded from helpers

* Nits
2018-08-09 13:02:06 -04:00
Florent Vilmart 5d91c1057f Updates many dependencies (#4938) 2018-08-07 12:57:06 -04:00
Florent Vilmart 1aaead319e fix(package): update request to version 2.87.0 (#4935)
Closes #4634
2018-08-07 11:31:18 -04:00
Florent Vilmart 444f67d219 Greenkeeper/commander 2.17.1 (#4936)
* fix(package): update commander to version 2.17.1

Closes #4927

* chore(package): update lockfile

https://npm.im/greenkeeper-lockfile
2018-08-07 11:31:00 -04:00
Florent Vilmart a021fef830 Adds exposed headers to avoid issue in JS SDK (#4934)
* Adds exposed headers to avoid issue in JS SDK

* Adds test for headers
2018-08-07 11:13:15 -04:00
Florent Vilmart ff25ae254d Update parse SDK to 2.0.0 (#4925)
* WIP: Integrate JS SDK v2

- Removes backbone style callbacks
- Use Promise instead of Parse.Promise

* Fixes ParseObject and ParseRelation

* Updates Parse.Query with promises

* Alls tests should pass

* Ensure a fresh user is used for each test

* Use REST implementation to avoid side effects for username/email duplicates

* Uses js sdk v2
2018-08-05 13:58:07 -04:00
GabrielLomba a61ef7ee2f Fix typo in FilesController.spec (#4917)
* Add the addFileNameHash option that allows users to remove the hash from file names

* Change option name to preserveFileName

* Revert changes to package-lock.json

* Fix typo in FileController.spec
2018-07-27 17:41:04 -04:00
Florent Vilmart dd84c64d16 Pins uws due to drame (#4916) 2018-07-27 09:04:28 -04:00
GabrielLomba 6a151ee135 Add the addFileNameHash option that allows users to remove the hash f… (#4915)
* Add the addFileNameHash option that allows users to remove the hash from file names

* Change option name to preserveFileName

* Revert changes to package-lock.json
2018-07-27 09:04:06 -04:00
greenkeeper[bot] 0363728b83 Update ws to the latest version 🚀 (#4909)
* fix(package): update ws to version 6.0.0

* chore(package): update lockfile

https://npm.im/greenkeeper-lockfile
2018-07-27 08:19:02 -04:00
Florent Vilmart a2c40367fa Ensures the test suites are properly formed for vscode jasmine plugin (#4901)
* Ensures the test suites are properly formed for vscode jasmine plugin

* nit
2018-07-19 07:05:27 +00:00
Florent Vilmart efb6c63a95 Ensure legacy users with authData are not locked out (#4898)
* Adds fix for issue, ensuring legacy users with no ACL are properly handled

* Runs tests only on mongo
2018-07-18 14:42:50 +00:00
Saulo Tauil b22947d4ec Enable express error handler (#4697)
* Propagate error to express handler in all situations

* Call the default error handler if `enableExpressErrorHandler` is truthy

* Updating options interface and definitions

* Testing express error handler

* Test spec fixes

* Fix test
2018-07-17 19:47:00 +00:00
Ross Bayer ced6b76ef5 Support incrementing push badge value by more than 1 (#4889)
* Support 'IncrementByN' badge value for higher push badge increments

* Fix test

* Rely on object for badge incrementation (i.e. {increment: 3}) rather than string (IncrementBy3)

* For badge incrementation, utilize format similar to other operation notation
2018-07-12 20:34:08 +02:00
greenkeeper[bot] 800959f458 Update flow-bin to the latest version 🚀 (#4877)
* chore(package): update flow-bin to version 0.76.0

* chore(package): update lockfile

https://npm.im/greenkeeper-lockfile
2018-07-12 14:23:05 +00:00
greenkeeper[bot] 8b571a3b54 Update bcrypt to the latest version 🚀 (#4875)
* chore(package): update bcrypt to version 3.0.0

* chore(package): update lockfile

https://npm.im/greenkeeper-lockfile
2018-07-12 13:59:52 +00:00
Florent Vilmart c1e32d853f Greenkeeper/nodemon 1.18.1 (#4891)
* chore(package): update nodemon to version 1.18.1

Closes #4885

* chore(package): update lockfile

https://npm.im/greenkeeper-lockfile
2018-07-12 13:59:06 +00:00
greenkeeper[bot] cad81f83e3 Update ws to the latest version 🚀 (#4888)
* fix(package): update ws to version 5.2.2

* chore(package): update lockfile

https://npm.im/greenkeeper-lockfile
2018-07-12 13:57:52 +00:00
greenkeeper[bot] e07a442362 Update mongodb to the latest version 🚀 (#4874)
* fix(package): update mongodb to version 3.1.1

* chore(package): update lockfile

https://npm.im/greenkeeper-lockfile
2018-07-06 06:18:43 -04:00
Florent Vilmart 01323abbc8 Greenkeeper/flow bin 0.75.0 (#4872)
* chore(package): update flow-bin to version 0.75.0

Closes #4816

* chore(package): update lockfile

https://npm.im/greenkeeper-lockfile
2018-07-04 11:52:31 -04:00
greenkeeper[bot] 3819fe8367 Update mongodb-runner to the latest version 🚀 (#4869)
* chore(package): update mongodb-runner to version 4.0.0

* chore(package): update lockfile

https://npm.im/greenkeeper-lockfile
2018-07-03 11:14:26 -04:00
Florent Vilmart 960431b92d Even faster tests (#4868)
* Various improvements in test name / de-duplications

* Reverts to class by class deletion, introduced fast mode that just delete data for mongo

- Speeds up are incredible Executed 1695 of 1713 specs INCOMPLETE (18 PENDING) in 4 mins 19 secs.

* Adds documentation about the deleteEverything
2018-07-03 11:13:08 -04:00
Florent Vilmart ae1a8226d5 Removes need to use babel-register (#4865)
* Removes need to use babel-register

- Adds watch to watch changes when running the test to regenerate
- Tests are now pure node 8

* Adds timing to helper.js

* Update contribution guide

* Adds inline sourcemaps generation to restore coverage

* nits
2018-07-02 23:30:14 -04:00
Florent Vilmart 74740abead Update issue templates (#4867) 2018-07-02 22:15:55 -04:00
greenkeeper[bot] ed4a436860 Update pg-promise to the latest version 🚀 (#4866)
* fix(package): update pg-promise to version 8.4.5

* chore(package): update lockfile

https://npm.im/greenkeeper-lockfile
2018-07-02 20:47:17 -04:00
Florent Vilmart e97df073df Testing improvements (#4863)
* Adds supports-color so spec reporter dont output colors when not supported

* Adds babelrc so breakpoints dont jump around in specs

* Rename test to avoid duplicated name

* Removes unused methods

* Better naming for it_exclude
2018-07-01 00:00:37 -04:00
Florent Vilmart 585938ca2c improves test performance on mongodb (#4862)
* improves test performance on mongodb

* Removes unused methdos
2018-06-29 17:09:51 -04:00
greenkeeper[bot] 1e8ba742a8 Update commander to the latest version 🚀 (#4861)
* fix(package): update commander to version 2.16.0

* chore(package): update lockfile

https://npm.im/greenkeeper-lockfile
2018-06-29 08:12:11 -04:00
Florent Vilmart 3bcb5a0997 Ensure User ACL's are more flexible and secure #3588 (#4860)
* Fixes an issue that would let the beforeDelete be called when user has no access to the object

* Ensure we properly lock user

- Improves find method so we can attempt to read for a write poking the right ACL instead of using masterKey
- This ensure we do not run beforeDelete/beforeFind/beforeSave in the wrong scenarios

* nits

* Caps insufficient
2018-06-28 16:31:22 -04:00
Florent Vilmart 9e5d26ebd2 Removes unused package multer, fixes audit (#4859) 2018-06-28 09:22:48 -04:00
greenkeeper[bot] d2e3459206 Update cross-env to the latest version 🚀 (#4830)
* chore(package): update cross-env to version 5.2.0

* chore(package): update lockfile

https://npm.im/greenkeeper-lockfile
2018-06-28 08:07:35 -04:00
greenkeeper[bot] 5add9584a3 Update multer to the latest version 🚀 (#4858)
* fix(package): update multer to version 1.3.1

* chore(package): update lockfile

https://npm.im/greenkeeper-lockfile
2018-06-28 08:06:14 -04:00
greenkeeper[bot] b332f4d4ae Update mongodb to the latest version 🚀 (#4855)
* fix(package): update mongodb to version 3.1.0

* chore(package): update lockfile

https://npm.im/greenkeeper-lockfile

* starting mongo 3.1.0, read preferences are passed again

* Adds test confirming #4831 is properly functional now
2018-06-27 14:12:51 -04:00
Chris 5cdbde244a #4678: Converting strings to Date when schema.type is Date within agg… (#4743)
* #4678: Converting strings to Date when schema.type is Date within aggregate function

* Added test cases to test new date match aggregate query

* Added function to parse match aggregate arguments and convert necessary values to Date objects

* Added missing return value

* Improved code quality based on suggestions and figured out why tests were failing

* Added tests from @dplewis

* Supporting project aggregation as well as exists operator

* Excluding exists match for postgres

* Handling the $group operator similar to $match and $project

* Added more tests for better code coverage

* Excluding certain tests from being run on postgres

* Excluding one more test  from postgres

* clean up
2018-06-26 17:18:04 -05:00
greenkeeper[bot] 3ace26da6f Update eslint to the latest version 🚀 (#4848)
* chore(package): update eslint to version 5.0.0

* chore(package): update lockfile

https://npm.im/greenkeeper-lockfile
2018-06-25 21:13:10 -04:00
Florent Vilmart e15244c7b2 Removes npm-git.sh scripts (#4853) 2018-06-25 20:59:54 -04:00
greenkeeper[bot] ade5a33c27 Update ws to the latest version 🚀 (#4849)
* fix(package): update ws to version 5.2.1

* chore(package): update lockfile

https://npm.im/greenkeeper-lockfile
2018-06-25 15:04:11 -04:00
Diamond Lewis 64388a31ad Aggregate allow multiple of same stage (#4835)
* Aggregate Allow Multiple Stages

* remove testing files

* nit

* spread them
2018-06-23 11:28:17 -05:00
Diamond Lewis 0e93306867 nit 2018-06-16 10:16:54 -07:00
Diamond Lewis 00252cbeff add includeAll option 2018-06-16 10:16:54 -07:00
Johnny 2ef437a2bc Added verify password to users router and tests. (#4747)
* Added verify password to users router and tests.

* Added more tests to support more coverage.

* Added additional tests to spec. Removed condition from verifyPassword function where authData null keys condition wasn't necessary.

* Removed POST handling from verifyPassword.

* Refactored handleLogin and handleVerifyPassword to use shared helper function to validate the password provided in the request.

* Refactored verifyPassword and login to not use try/catch. Parent promise returns the error. Moved login specific functions to login handler.

* Added account lockout policy to verify password function. Added test spec for account lockout in verify password.

* no message

* Merged new changes from master. Made changes as requested from comments.

* We cannot remove hidden properties from the helper before returning to the login function. The password expiration check in the login function is dependent on some hidden properties, otherwise three password policy tests fail.
2018-06-13 14:19:53 -04:00
Florent Vilmart 9c9c07fb26 Ensure read preference is never overriden, so DB config prevails (#4833) 2018-06-13 13:51:29 -04:00
Manuel 1e29d0299b add support for geoWithin.centerSphere queries via withJSON (#4825)
* add support for geoWithin.centerSphere queries via withJSON

* added test for passing array of lat, lng instead of Parse.GeoPoint

* added postgres support

* added more tests

* improved tests and validation

* added more tests
2018-06-12 12:41:02 -04:00
Arthur Cinader 260c466dcb Change name of function and variable from baseFieldName to rootFieldName (#4817) 2018-06-07 19:27:11 -04:00
Arthur Cinader e06471603f Don't error when attempting to sort on an object field (#4806)
* add failing test to demonstrate that you can't sort on a
field in an object.

* Only validate the base of the field name.

* fix test name

* Only test sort for mongo.

* pg order by nested object

* level 2 test

* Factor out operation to get a field's base name.  Add comment.

* tweak comment wording so it wont make my grammar teacher angry.
2018-06-07 18:47:18 -04:00
Jaeger cf3a872e67 Don't merge JSON fields after save() when using Postgres to keep same behaviour as MongoDB (#4808) (#4815) 2018-06-07 12:43:54 -04:00
Florent Vilmart 1afc34eea3 chore(package): update nyc to version 12.0.2 (#4807)
* chore(package): update nyc to version 12.0.2

Closes #4804

* chore(package): update lockfile

https://npm.im/greenkeeper-lockfile
2018-06-04 15:22:29 -04:00
Florent Vilmart 0c2f1d583b ⚡ Release 2.8.2 (#4802) 2018-06-04 13:07:52 -04:00
greenkeeper[bot] 3b40d2227d Update flow-bin to the latest version 🚀 (#4786)
* chore(package): update flow-bin to version 0.73.0

* chore(package): update lockfile

https://npm.im/greenkeeper-lockfile
2018-06-01 09:32:33 -04:00
Tyler Brock c5e8470e36 Always use http-agent for hooks and triggers (#4800) 2018-06-01 09:31:10 -04:00
Florent Vilmart 9bff44b446 Ensure users with undefined ACL are treated as readable (#4795)
* Adds test to reproduce issue #4790

* Attempt to allow failure on node STABLE

* Use new format for apt packages
2018-05-30 12:55:15 -04:00
Tyler Brock f4422c491e Update defs for new hookKeepAlive config option (#4794)
All tests pass on re-run. :-)
2018-05-29 17:30:26 -07:00
Tyler Brock 82fb06f9c9 Use http agents for hook requests (#4791) 2018-05-29 17:48:06 -04:00
greenkeeper[bot] 32d8c36ff5 Update cross-env to the latest version 🚀 (#4780)
* chore(package): update cross-env to version 5.1.6

* chore(package): update lockfile

https://npm.im/greenkeeper-lockfile
2018-05-24 16:28:37 -04:00
greenkeeper[bot] fd862950e2 Update nodemon to the latest version 🚀 (#4781)
* chore(package): update nodemon to version 1.17.5

* chore(package): update lockfile

https://npm.im/greenkeeper-lockfile
2018-05-24 16:28:17 -04:00
Mads Bjerre 57da2def1d Adds withinPolygon support for Polygon object (#4067)
* Whitespace

* Add Polygon type to $polygon query

* Add tests

Polygon object in $polygon query
$geoIntersects queries

* Refactor

* Postgres support

* More tests

* Remove duplicate test

* Missing semicolon

* fix tests
2018-05-22 12:06:43 -04:00
Florent Vilmart 4dcbc4245b fix(package): update ws to version 5.2.0 (#4779)
* fix(package): update ws to version 5.2.0

* chore(package): update lockfile

https://npm.im/greenkeeper-lockfile
2018-05-21 21:43:50 -04:00
Florent Vilmart 642e2ffe12 Adds greenkeeper package-lock management (#4778) 2018-05-21 17:38:54 -04:00
greenkeeper[bot] d2b04ddd92 chore(package): update gaze to version 1.1.3 (#4776) 2018-05-21 15:45:10 -04:00
greenkeeper[bot] 33173c4d9b fix(package): update deepcopy to version 1.0.0 (#4775) 2018-05-20 15:13:17 -04:00
Florent Vilmart ff0c04ffa3 chore(package): update deep-diff to version 1.0.1 (#4774)
Closes #4733
2018-05-19 11:24:58 -04:00
greenkeeper[bot] df274dcca9 fix(package): update commander to version 2.15.1 (#4663) 2018-05-18 15:49:40 -04:00
Florent Vilmart a619119684 Bumps minimum node engine to 8+ (#4474)
* Use node 8 as development environment

* fixup! Use node 8 as development environment

* bump node to 8.10

* Targets node 8 for everything

* Run npm install so lock file is up to date

* Use push adapter v3

* Deflake a test on ParseUser

* Adds slight delay after logout

* Ensure we wait even if call fails

* Use node carbon
2018-05-18 15:49:00 -04:00
Jérémy Piednoel 77ed10f57b Adding $nor operator support (#4768)
* adding nor to specialQuerykeys

* adding nor suport

* adding test

* CRLF

* adding postgres NOR

* adding nor validation

* adding NOR test

* adding test amd fixing NOR cases

* revert the nor logic
2018-05-18 14:26:33 -05:00
Florent Vilmart a9ebc2b9af ⚡ Release 2.8.1 (#4771)
* unfuck release 2.8.0

* ⚡ Release 2.8.1

* Ensure we can install also on latest
2018-05-18 13:06:12 -04:00
Florent Vilmart 8fe3265c74 Release 2.8.0 (#4767)
* Ensure issue template has proper line endings

* Bumps to 2.8.0

* Adds Changelog
2018-05-18 10:55:20 -04:00
Saulo Tauil 95550f4c77 Making response.error accept a Parse.Error object as parameter (#4695) 2018-05-18 10:15:25 -04:00
Florent Vilmart fc47f9b84f issue(afterFind): Fixes issue when using afterFind with relations (#4752)
* Adds failing test for the issue

* Adds fix for the issue
2018-05-18 09:36:56 -04:00
Jérémy Piednoel c0f86ae1d1 Adding Mongodb element to add arrayMatches the #4762 (#4766)
* Adding elemMatch and nor

* lint

* adding test

* adding edge test

* postgres support

* clean up

* empty test
2018-05-18 08:35:50 -05:00
Florent Vilmart ad244d6654 feature: User Lockout (#4749)
* Allows masterKey to lock _User object and prevent login with email / password

* Ensure the authData based auth can be locked out as well when accounts is masterKey only
2018-05-16 15:40:02 -04:00
greenkeeper[bot] bfd0c4bf2f chore(package): update cross-env to version 5.1.5 (#4757) 2018-05-16 15:39:40 -04:00
greenkeeper[bot] d36c7c7f90 chore(package): update uws to version 10.148.0 (#4758) 2018-05-16 15:39:29 -04:00
greenkeeper[bot] 10232673d9 fix(package): update body-parser to version 1.18.3 (#4763) 2018-05-16 15:39:10 -04:00
Eduard Bosch Bertran c0e3672e32 New query condition support to match all strings that starts with some other given strings (#3864)
* feat: Convert $regex value to RegExp object

* feat: Add lib folder

* Revert "feat: Add lib folder"

This reverts commit c9dfbcbf69.

* feat: Add $regex test in $all array

* test: Test regex with $all only in MongoDB

* Revert "test: Test regex with $all only in MongoDB"

This reverts commit d7194c7869.

* feat: Add tests for containsAllStartingWith

* feat: Add postgres support

Thanks to @dplewis

* feat: Check that all values in $all must be regex or none

* test: Check that $all vaules must be regex or none

* feat: Update tests to use only REST API

* refactor: Move $all regex check to adapter

* feat: Check for valid $all values in progres

* refactor: Update function name

* fix:  Postgres $all values regex checking

* fix: Check starts with as string

* fix: Define contains all regex sql function

* fix: Wrong value check

* fix: Check valid data

* fix: Check regex when there is only one value

* fix: Constains all starting with string returns empty with bad params

* fix: Pass correct regex value

* feat: Add missing tests

* feat: Add missing tests

* feat: Add more tests

* fix: Unify MongoDB and PostgreSQL functionality

* fix: Lint checks

* fix: Test broken

$regex in $all list must be { $regex: "string" }

* test for empty $all
2018-05-15 20:42:32 -05:00
Joseph Frazier 2c357df33e Fix typo: "requrest" -> "request" (#4761) 2018-05-13 13:49:07 -04:00
Florent Vilmart 3999de7e0b Update issue templates 2018-05-08 16:40:07 -04:00
Florent Vilmart a3e5b20536 Bumps @parse/simple-mailgun-adapter (#4753) 2018-05-08 13:48:51 -04:00
Florent Vilmart bb1641419f fix(package): update mime to version 2.3.1 (#4721)
Closes #4716
2018-05-01 14:02:56 -04:00
Florent Vilmart 0b69700f18 chore(package): update flow-bin to version 0.71.0 (#4746)
Closes #4730
2018-05-01 13:32:12 -04:00
greenkeeper[bot] ac2695e196 chore(package): update deep-diff to version 1.0.0 (#4733) 2018-05-01 13:31:47 -04:00
Florent Vilmart 098d085621 fix(package): update request to version 2.85.0 (#4745)
Closes #4634
2018-05-01 13:31:02 -04:00
Vitaly Tomilov a1f5f37af7 upgrading pg-promise to 8.4.0 (#4736) 2018-05-01 13:27:29 -04:00
greenkeeper[bot] d48b30c734 fix(package): update mongodb to version 3.0.7 (#4732) 2018-05-01 13:26:04 -04:00
greenkeeper[bot] 3b5dff195f chore(package): update bcrypt to version 2.0.1 (#4735) 2018-05-01 13:25:37 -04:00
Florent Vilmart fad7b46c65 chore: Fixes issue related to flow types (#4724)
* Fixes issue related to flow types

* Improves type inference on where clause, index

* run parse-server start on liveQueryServer only on mongo, state leaks on postgres
2018-05-01 07:37:38 -04:00
Claire Neveu 04588bccb9 Allow creation of indices on default fields (#4738)
* Allow creation of indexes on default fields

* Update test

* Try to address flaky cache test
2018-04-25 16:06:10 -07:00
oleg b1017ac17c Send version for Vkontakte API (#4725) 2018-04-15 15:09:28 -04:00
Florent Vilmart ebf65bfc5d Updates @parse/push-adapter (#4723) 2018-04-15 09:52:18 -04:00
dblythy 22801d2d8f Ensure we respond with invalid password even if email is unverified (#4708) 2018-04-11 15:39:32 -04:00
Florent Vilmart 8380107e6b chore(package): update nodemon to version 1.17.3 (#4712)
Closes #4637
2018-04-10 13:57:11 -04:00
Florent Vilmart a77b0a238f chore(package): update flow-bin to version 0.69.0 (#4711)
Closes #4651
2018-04-10 13:56:46 -04:00
Florent Vilmart 6c81635188 fix(package): update mongodb to version 3.0.6 (#4710)
Closes #4674
2018-04-10 13:56:05 -04:00
Florent Vilmart fed40128a5 fix(package): update mime to version 2.2.2 (#4709)
Closes #4687
2018-04-10 13:55:21 -04:00
greenkeeper[bot] ee151de56a fix(package): update ws to version 5.1.1 (#4691) 2018-04-10 13:55:02 -04:00
Jong Eun Lee b0bcbd8731 add _password_history to default sensitive data (#4699) 2018-04-10 13:54:35 -04:00
greenkeeper[bot] 06be78802e chore(package): update bcrypt to version 2.0.0 (#4703) 2018-04-10 13:53:49 -04:00
Diamond Lewis 0fd8493929 Fix purging empty class (#4676)
* Fix purging empty class

* remove fit
2018-03-25 08:41:25 -05:00
Diamond Lewis 710848938f PG: Fixes issues comparing to zero or false (#4667)
* initial test

* more test

* clean up

* added correct errors

* test fix

* $eq null test

* type check
2018-03-24 17:45:34 -05:00
Samarth Verma 1a5c05b6a8 Fix organization url (#4639) 2018-03-21 14:54:08 -04:00
greenkeeper[bot] 306826fe0f fix(package): update ws to version 5.1.0 (#4662) 2018-03-21 14:52:50 -04:00
Diamond Lewis a3230011eb Check for node version in postinstall script (#4657)
Added open collective as a bonus.

I notice a lot of user still fork parse-server-example. They then update to the latest version but their engines.node in package.json stays the same.

This will output directions to support latest version.
2018-03-16 16:09:00 -05:00
SebC 6abf29dd97 Remove FB Graph API version from URL to use the oldest non deprecated version (2.5 for now, which will be deprecated soon) (#4650) 2018-03-15 14:05:41 -05:00
Diamond Lewis c36ef99368 Fix Aggregate Match Pointer (#4643) 2018-03-14 21:57:30 -05:00
Florent Vilmart 3acb3e7a9b ⚡ Release 2.7.4 (#4626)
* ⚡ Release 2.7.4

* Update package.json

* Update CHANGELOG.md
2018-03-10 16:40:59 -05:00
greenkeeper[bot] d852cade9c fix(package): update pg-promise to version 8.2.1 (#4624) 2018-03-10 16:23:07 -05:00
Diamond Lewis c6bc81caef Mongo: Fix reversing polygon coordinates (#4609)
* Fix reversing polygon coordinates

* comments fix

* real data test

* improved tests
2018-03-10 14:27:03 -06:00
Florent Vilmart c7235829c3 chore(package): update nodemon to version 1.17.1 (#4623)
Closes #4578
2018-03-10 12:03:17 -05:00
Florent Vilmart 0569ddd8d3 ⚡ Release 2.7.3 (#4622) 2018-03-10 11:58:27 -05:00
greenkeeper[bot] 3f19540f1a chore(package): update cross-env to version 5.1.4 (#4617) 2018-03-10 10:08:41 -05:00
greenkeeper[bot] c12d430c43 fix(package): update winston to version 2.4.1 (#4618) 2018-03-10 10:08:18 -05:00
greenkeeper[bot] c737f794a0 fix(package): update pg-promise to version 8.2.0 (#4620) 2018-03-10 10:07:54 -05:00
AreyouHappy d768211ab9 fix purge command issue affecting readonly masterKey (#4619) (#4621) 2018-03-10 10:07:31 -05:00
greenkeeper[bot] 673039fc5a fix(package): update commander to version 2.15.0 (#4611) 2018-03-08 17:30:59 -05:00
greenkeeper[bot] e8c8151277 fix(package): update lru-cache to version 4.1.2 (#4614) 2018-03-08 17:30:43 -05:00
greenkeeper[bot] 38f57c3fcb chore(package): update flow-bin to version 0.67.1 (#4612) 2018-03-08 17:30:32 -05:00
Arthur Cinader d19ac077b6 Live query defaults reference ParseServer instead of LiverQuery server. (#4613)
* fix workding for live query defaults help

* generate the definition file.
2018-03-08 17:30:18 -05:00
Diamond Lewis f0674df424 Fixes Issue unsetting in beforeSave doesn't allow object creation (#4610)
* skip unset fields on canAddField

* removed fit

* add null check
2018-03-08 10:31:53 -06:00
Stephen Tuso f03e35ff76 Fix cloud env var in readme (#4584) 2018-03-06 19:27:09 -05:00
greenkeeper[bot] 887adea4dd chore(package): update jasmine to version 3.1.0 (#4592) 2018-03-06 19:26:09 -05:00
greenkeeper[bot] d7a554e426 fix(package): update parse to version 1.11.1 (#4605) 2018-03-06 19:25:46 -05:00
Florent Vilmart e785f07876 fix(package): update ws to version 5.0.0 (#4604)
Closes #4585
2018-03-06 10:43:25 -05:00
Florent Vilmart 909ead487c fix(package): update mongodb to version 3.0.4 (#4603)
Closes #4586
2018-03-06 10:43:00 -05:00
Florent Vilmart 7feafb9eea fix(package): update pg-promise to version 8.1.1 (#4602)
Closes #4587
2018-03-06 10:42:31 -05:00
Florent Vilmart 60eea59c76 Handle mongodb auth errors that may occur after running the server for a while (#4576) 2018-03-02 10:24:58 -05:00
maciej-ka 9db63a4a50 fix: failing tests with dates in local time 2018-03-01 10:24:24 -08:00
Diamond Lewis d8f3fb16bd Skip afterFind for Aggregate and Distinct Queries (#4596) 2018-02-28 19:32:01 -06:00
6thfdwp 213801c4b1 auth: add adapter for Facebook accountkit login (#4434)
* Integrate auth adapter for Facebook accountkit login

* Also verify Facebook app id associated with account kit login

* Add appsecret_proof as extra graph request parameter

* Specific error message for Account kit and more test coverage

* One more test to cover when AppIds for Facebook account kit not configured properly
2018-02-23 07:37:55 -05:00
greenkeeper[bot] 55f4b0f7c5 fix(package): update pg-promise to version 7.5.4 (#4577) 2018-02-20 08:27:20 -05:00
Florent Vilmart 647d8e5bff chore(package): update babel-eslint to version 8.2.2 (#4579) 2018-02-20 08:26:43 -05:00
Florent Vilmart 11c40dce97 Adds session creation code in Auth.js (#4574) 2018-02-19 11:15:54 -05:00
Florent Vilmart b754d51e8e chore(package): update jasmine to version 3.0.0 (#4553)
* chore(package): update jasmine to version 3.0.0

Closes #4547

* Fixes failing tests for jasmine 3.0

Starting 3.0, done(something) will fail

* Update tests so they dont leverage var, but let and const

With jasmine 3.0, the randomization engine was making the test fails because of the scope of `var`

* Remove randomizer

* Use same adapter for PG tests, drop table to ensure the tests dont side effect
2018-02-17 09:55:30 -05:00
greenkeeper[bot] 8ec7785d53 chore(package): update nodemon to version 1.15.0 (#4569) 2018-02-16 16:21:28 -05:00
greenkeeper[bot] 7ab5da171e fix(package): update pg-promise to version 7.5.3 (#4571) 2018-02-16 16:20:56 -05:00
greenkeeper[bot] ab669a21e7 chore(package): update flow-bin to version 0.66.0 (#4572) 2018-02-16 16:20:41 -05:00
Diamond Lewis 143b0f01cf PG: Add dates to group aggregate (#4549)
* PG: Add dates to group aggregate

* returns dates as UTC
2018-02-16 12:41:02 -06:00
Juan Camilo Guarin Peñaranda 10eafe922e Added docs for objectIdSize (#4526) 2018-02-16 10:45:06 -05:00
Diamond Lewis 848a6cf7ae Distinct support for null (#4559)
* distinct support for null

* better testing
2018-02-16 10:44:42 -05:00
Florent Vilmart cac14bce09 fix(package): update pg-promise to version 7.5.2 (#4570)
Closes #4525
2018-02-16 08:41:00 -05:00
greenkeeper[bot] 3b5a4579d1 chore(package): update flow-bin to version 0.65.0 (#4548) 2018-02-16 08:40:18 -05:00
fridays 8be7491595 Fix server crash on invalid LiveQuery socket event (#4533)
* add failing test for missing command message

* don't mock tv4.validate

* fix existing tests

* make request.op required

* Update comment
2018-02-16 08:40:04 -05:00
Florent Vilmart ae349f3888 fix(package): update commander to version 2.14.1 (#4552)
Closes #4545
2018-02-16 08:39:03 -05:00
greenkeeper[bot] c85c90a454 fix(package): update mongodb to version 3.0.2 (#4529) 2018-02-16 08:38:40 -05:00
Diamond Lewis 3cd77eeb7c duplicate value on unique index error (#4560) 2018-02-09 07:39:35 -06:00
greenkeeper[bot] c16e6bb5e0 fix(package): update lodash to version 4.17.5 (#4543) 2018-02-07 22:29:39 -05:00
greenkeeper[bot] 90fd051ba4 chore(package): update nodemon to version 1.14.12 (#4539) 2018-02-07 22:29:23 -05:00
Tyson Andre 83a0d7b685 Fix "undefined property '__op'" in postgres update (#4541)
* Fix "undefined property '__op'" in postgres update

This causes a TypeError which becomes a regular Error,
before the update can be issued. (I think)

This happens when there is an object schema,
and there is also an unrelated field in originalUpdate
which is null or undefined.

e.g. when 'location' is a mandatory object in postgres,
and 'middleName' is an optional string,
PostgresStorageAdapter would throw when a query similar
to the below was performed:
(Object.keys(originalUpdate) would include "middleName" as a value of `k`)

    query.set('location', {'country': 'US'})
    query.set('middleName', undefined);

* Fix lint error
2018-02-07 08:16:54 -05:00
Benjamin Wilson Friedman db8594dd33 Regenerate Email Verification Token on Email Request (#4439)
* regenerate email verification token & expiration in /verificationEmailRequest

* Remove password field when saving on postgres
2018-02-01 18:35:08 -05:00
Paulo Reis f2e21b0538 Additional test for emailVerified set to false on email changed from existing (#4532)
* Create a test to check the issue #4501
* Check if after the user confirms the email and change the email again a new verification email is sent
* Change the spec text to requested in PR
2018-02-01 14:39:57 -08:00
Florent Vilmart 550b69e271 ⚡ Release 2.7.2 (#4521)
* ⚡ Release 2.7.2

* Update CHANGELOG.md
2018-01-24 15:57:36 -05:00
Florent Vilmart edba18fa9d fix(package): update commander to version 2.13.0 (#4522)
Closes #4394
2018-01-24 15:50:41 -05:00
Florent Vilmart 401cc357e5 Fixes issue affecting sorting in beforeFind (#4519)
* Fixes issue affecting sorting in beforeFind

* Update CloudCode.spec.js
2018-01-24 15:15:05 -05:00
greenkeeper[bot] 40407fea80 fix(package): update commander to version 2.12.2 (#4394) 2018-01-24 15:07:18 -05:00
greenkeeper[bot] 44086c96e6 chore(package): update flow-bin to version 0.64.0 (#4520) 2018-01-24 15:07:00 -05:00
greenkeeper[bot] 0807845f57 fix(package): update semver to version 5.5.0 (#4504) 2018-01-24 09:32:28 -05:00
Florent Vilmart bcca1b6497 chore(package): update nodemon to version 1.14.11 (#4518)
Closes #4476
2018-01-24 09:24:07 -05:00
greenkeeper[bot] 4da27773fd fix(package): update ws to version 4.0.0 (#4485) 2018-01-24 09:23:44 -05:00
Florent Vilmart db3777ec15 chore(package): update flow-bin to version 0.63.1 (#4515)
Closes #4486
2018-01-20 23:41:39 -05:00
greenkeeper[bot] 4eda0a2024 chore(package): update jasmine to version 2.9.0 (#4509) 2018-01-20 23:41:01 -05:00
greenkeeper[bot] 51e1cbad47 fix(package): update pg-promise to version 7.4.0 (#4514) 2018-01-20 23:40:41 -05:00
Diamond Lewis 64e568d000 Improved match aggregate (#4495)
* Improve aggregate queries

* using pg-promise query formatting

* match multiple comparison

* $or and complex match
2018-01-20 08:00:36 -06:00
Florent Vilmart 33890bbbfc Do not mark the empty pushs as failed (#4497) 2018-01-13 15:12:19 -05:00
greenkeeper[bot] 75fa16aa7e fix(package): update mime to version 2.2.0 (#4482) 2018-01-13 10:15:00 -05:00
greenkeeper[bot] 433e169055 fix(package): update pg-promise to version 7.3.3 (#4487) 2018-01-13 10:14:46 -05:00
Diamond Lewis 8ec07b83d0 Support pointer in aggregate query (#4493) 2018-01-08 20:42:08 -06:00
Paulo Reis cb8f038ca8 #4338 pg schema upgrade (#4375)
* Method to upgrade schemas in Postgres;

Adds:
  - schemaUpgrade method which check the fields to add and delete from old schema;

* Remove the columns delete in schemaUpgrade method;

* ESLint fix and PostgresStorageAdapter.schemaUpgrade spec test

Adds:
  - Add PostgresStorageAdapter.schemaUpgrade spec tests: creates a table,
    simulates the addition of a new field and checks if its present in the database
Chores:
  - ESLint eol-last fix;

* Add check columns before and after schema upgrade, and remove the unnecessary piece of code

Add:
  - Check the right columns is present before schema upgrade and the new field is not,
    then check if the right columns is present and the new field;
Remove:
  - Piece of code unnecessary because it not need to remove diff columns;

* Optimize the schemaUpgrade method following @vitaly-t instructions, and more tests;

* If the class does not have any columns and needs an upgrade the code would
return without doing so. Fixed this.

Chore:
  - Allows class with no column to be upgraded;
  - Test for class with no columns being upgraded;

* Update PostgresStorageAdapter.js

rewriting method schemaUpgrade

* Update PostgresStorageAdapter.spec.js

* Update PostgresStorageAdapter.spec.js
2018-01-03 03:23:05 +00:00
Vitaly Tomilov fc6a2fddc4 New Year cosmetics :) (#4475)
* cosmetics

* making nicer promise pattern + if->else blocks
* removing return if unwanted data from an `UPDATE`

* Update PostgresStorageAdapter.js

* Update PostgresStorageAdapter.js

* Update PostgresStorageAdapter.js

Restoring the `UPDATE` result, as apparently it is in fact used. Ouch! 😄

* Update PostgresStorageAdapter.js
2018-01-01 20:33:41 +00:00
Florent Vilmart 10631371e6 Introduces flow types for storage (#4349)
* Introduces flow types for storage

* Better typing of QueryOptions

* Adds flow types to SchemaCOntroller,

- runs flow on pre tests
- fixes flow

* Adds ClassLevelPermissions type

* Moves Controller types into a single file

* Changes import styles

* Changes import styles

* fixing method setIndexesWithSchemaFormat (#4454)

Fixing invalid database code in method `setIndexesWithSchemaFormat`:

* It must be a transaction, not a task, as it executes multiple database changes
* It should contain the initial queries inside the transaction, providing the context, not outside it;
* Replaced the code with the ES6 Generator notation
* Removing the use of batch, as the value of the result promise is irrelevant, only success/failure that matters

* nits

* Fixes tests, improves flow typing
2017-12-30 20:44:18 -05:00
greenkeeper[bot] f0f1870aa3 Update mongodb to the latest version 🚀 (#4449)
* fix(package): update mongodb to version 3.0.0

* Compatibility with MongoDB client 3.0

* Updates Gridstore as well

* Set Read preference to Primary when not specified, to match original implementation

* Update MongoStorageAdapter.js

* Bumps to 3.0.1
2017-12-30 00:23:43 -05:00
Diamond Lewis 6143988a82 Support pointer in distinct query (#4471)
* Support pointer in distinct query

* extract transform pointer string
2017-12-29 21:32:40 -06:00
Florent Vilmart 7d773a5d8a fix(package): update mime to version 2.1.0 (#4446)
Closes #4443
2017-12-29 15:31:14 -05:00
Florent Vilmart e3a26540da chore(package): update nodemon to version 1.14.6 (#4472)
Closes #4417
2017-12-29 15:30:56 -05:00
greenkeeper[bot] c549663c96 fix(package): update mime to version 2.0.5 (#4443) 2017-12-29 13:43:54 -05:00
greenkeeper[bot] 58f32c4745 fix(package): update ws to version 3.3.3 (#4429) 2017-12-29 13:27:43 -05:00
Florent Vilmart 2ee68d3a8b chore(package): update cross-env to version 5.1.3 (#4445)
Closes #4441
2017-12-29 13:27:16 -05:00
Benjamin Wilson Friedman 7a9d4044af Scrub Passwords with URL Encoded Characters (#4433)
* scrub passwords with url encoded characters from logs

* compose query string from parsed params, redacting based on key if needed
2017-12-29 13:16:32 -05:00
Diamond Lewis 04f8673edd PG: Support for multiple projection in aggregate (#4469) 2017-12-29 17:39:16 +00:00
Vitaly Tomilov 6ba939994d consistent error throwing (#4470)
should use `throw` inside `.catch`.
2017-12-29 17:06:55 +00:00
Vitaly Tomilov bb053301e5 refactoring method createClass (#4465)
* refactoring method createClass

Removing error-analysis conditions that are irrelevant, i.e. since we are only returning the batch, the error will always have `data` set to the size of the input array, as per [the library's API](http://vitaly-t.github.io/spex/errors.BatchError.html).

* Update PostgresStorageAdapter.js

removing the use of the default separator.
2017-12-28 01:43:34 +00:00
Vitaly Tomilov 7bb9c6da59 refactoring deleteAllClasses (#4464)
* refactoring deleteAllClasses

...to re-use the connection.

* Update PostgresStorageAdapter.js

removing extra spaces.
2017-12-27 22:41:50 +00:00
Vitaly Tomilov b4aca8afc9 Reuse connection in getAllClasses (#4463)
as per the title.
2017-12-27 20:44:11 +00:00
Vitaly Tomilov 5681b3f902 Refactoring method addFieldIfNotExists (#4461)
* Refactoring method `addFieldIfNotExists`

* Using ES6 Generators syntax
* Passing in the context into method `createClass`, to reuse the connection
* Extending method `createClass` to reuse connections

* Update PostgresStorageAdapter.js

forgot: extending method `createClass` to reuse the connection.

* Update PostgresStorageAdapter.js

fixing the re-throw logic.
2017-12-27 18:14:15 +00:00
Vitaly Tomilov 820ae2b894 Better connection use in setClassLevelPermissions (#4460)
Method `setClassLevelPermissions` should use `.task` to share the connection for the two consecutive operations. It doesn't need a transaction, because the first operation does not need to roll back when the second one fails.
2017-12-27 00:30:50 +00:00
Vitaly Tomilov a868beda93 Refactoring method createTable (#4456)
* Refactoring method createTable

Replacing the weird task + transaction chain, by replacing it with just one transaction that encapsulates the complete logic.

* Update PostgresStorageAdapter.js

correcting the sequence to match the original exactly.

* Update PostgresStorageAdapter.js

Nesting the transaction inside a task, so it can execute successfully no matter if the containing task succeeds or fails.

* Update PostgresStorageAdapter.js

adding the missing bracket.
2017-12-26 00:14:16 +00:00
Vitaly Tomilov 34453bff46 PostgreSQL performance optimization (#4457)
Performance-optimizing transactions to execute all non-result queries as a single operation.
2017-12-25 21:08:04 +00:00
Vitaly Tomilov 415ee3a1fd fixing method setIndexesWithSchemaFormat (#4454)
Fixing invalid database code in method `setIndexesWithSchemaFormat`:

* It must be a transaction, not a task, as it executes multiple database changes
* It should contain the initial queries inside the transaction, providing the context, not outside it;
* Replaced the code with the ES6 Generator notation
* Removing the use of batch, as the value of the result promise is irrelevant, only success/failure that matters
2017-12-24 19:03:35 +00:00
Vitaly Tomilov a2b2f18927 db connection context fix (#4453)
fixing use of the wrong connection context inside a task
2017-12-24 17:56:23 +00:00
Vitaly Tomilov e0a31d2aeb improving database logic. (#4451)
improving database logic.
2017-12-24 16:34:01 +00:00
Vitaly Tomilov 08ba738227 refactoring database code (#4448)
* refactoring database code

Starting to refactor the database code for better use of promises + ES6 generators, to prepare for ES7 await/async.

* Update PostgresStorageAdapter.js

* Update PostgresStorageAdapter.js

naming the transaction.
2017-12-24 15:58:20 +00:00
Premith 33de770709 Add Parse Server Generic Email Adapter to README (#4101)
Added a generic email plugin to send Parse Server password reset and verification emails with generic accounts like Gmail. This module uses the nodemailer library.
2017-12-19 11:37:31 -08:00
Florent Vilmart bad217911c Adds ability to login with email when provided as username (#4420) 2017-12-12 08:49:45 -05:00
Florent Vilmart 8fd5f31089 Adds infos about vulnerability disclosures (#4413)
* Create SECURITY.md

* Update SECURITY.md

* Update SECURITY.md

* Update ISSUE_TEMPLATE.md

* Update ISSUE_TEMPLATE.md

* Clarify time table from contact to fix

* change times to days
2017-12-05 08:52:13 -05:00
greenkeeper[bot] 0bf69975de fix(package): update parse to version 1.11.0 (#4414) 2017-12-05 08:51:41 -05:00
Florent Vilmart 1f22ee36e7 ⚡ Release 2.7.1
(#4410)
* Adds failing test for 4409

* Adds fix

* ⚡ Release 2.7.1
2017-12-01 09:16:58 -05:00
Arthur Cinader ca542c3750 Fix test name to match what's tested 2017-11-30 12:40:39 -08:00
Arthur Cinader 1876d3f3fd Add doesNotMatchKeyInQuery case... 2017-11-30 12:40:39 -08:00
Henrik Malmberg 4ceff38811 added test for dot-notation in matchesKeyInQuery 2017-11-30 12:40:39 -08:00
Henrik Malmberg 741f869140 Allows to use dot-notation to match against a complex structure when using matchesKeyInQuery 2017-11-30 12:40:39 -08:00
Vitaly Tomilov 9d1d8515fc pg-promise refactoring (#4401)
initial refactoring of `pg-promise` code.
2017-11-30 08:30:15 +07:00
Addison Elliott 9eed81e636 Remove nsp check from Travis YML (#4403)
nsp is done through a GitHub PR hook and managed separately from Travis CI
2017-11-29 16:01:27 -05:00
Florent Vilmart e2d33678a9 Makes all scripts cross platforms (#4383) 2017-11-27 23:22:07 -05:00
Florent Vilmart 6102648855 ⚡ Release 2.7.0 (#4385)
* ⚡ Release 2.7.0

* Update CHANGELOG.md
2017-11-25 19:54:52 -05:00
Addison Elliott dd55bbe700 Fix eslint errors on Windows (#4364)
* Fix eslint errors on Windows

On Windows, when run `npm run lint`, a **LOT** of the following errors
appeared:
```
 error  Expected linebreaks to be 'LF' but found 'CRLF'  linebreak-style
```

This error only occurs on Windows because the `linebreak-style` eslint
setting was set to "unix". This is fixed by making the eslint config
file a Javascript script and setting the platform based on the build
platform.

Thus, the `linebreak-style` will be "windows" or "unix" depending on the
users platform.

* Remove linespace ending check in Lint

Add .gitattributes that will convert line endings automatically to LF when uploading to GitHub

* Remove bat file extension from gitattributes

Include lint check for line endings

* Change tabs to spaces

* Force LF line endings for each file upon downloading

Restore eslint linebreak style check for unix since LF line endings will be enforced

* Add a few more text files to the gitattributes

* Added png as binary file to gitattributes file

This was necessary because git was showing that the CRLF line endings were being converted to LF for PNG files on Windows
2017-11-25 16:06:52 -05:00
Diamond Lewis 4bccf96ae7 Add Indexes to Schema API (#4240)
* Add Indexes to Schema API

* error handling

* ci errors

* postgres support

* full text compound indexes

* pg clean up

* get indexes on startup

* test compound index on startup

* add default _id to index, full Text index on startup

* lint

* fix test
2017-11-25 15:39:31 -05:00
Florent Vilmart 6a1510729a Bump nodejs version to 6+ (#4272)
* let travis build against 3.x

* Cleanup dependencies and bump min version to current LTS

* Makes npm-git push all branches to -preview

* restores releases

* Bumps mime to 2.0.3 (requires node 6+)

* Bumps express to latest version

* Fixes linting issue after upgrade

* Use travis-branch for partial releases
2017-11-25 15:39:31 -05:00
Florent Vilmart ea57c523da let travis build against 3.x 2017-11-25 15:39:31 -05:00
Benjamin Wilson Friedman 219ad7224a Fixes sessionTokens being overridden in 'find' (#4332)
* remove session token replacement code

* adds cases for _User/_Session with sessionToken and with/without masterKey
2017-11-25 15:28:08 -05:00
Addison Elliott 2b9397a5a9 Update contributing.md (#4368)
* Update CONTRIBUTING.md

Clean up some of the language in the document.

Add command to run for testing on Windows

* Update CONTRIBUTING.md

Update coverage directory

* Update CONTRIBUTING.md

Add details about how to run test coverage
2017-11-25 15:20:16 -05:00
Florent Vilmart 4f56ec6265 fix(package): update commander to version 2.12.1 (#4382)
Closes #4371
2017-11-25 14:53:47 -05:00
greenkeeper[bot] cc48e5412e fix(package): update commander to version 2.12.0 (#4371) 2017-11-25 14:53:14 -05:00
greenkeeper[bot] c0215399a2 fix(package): update ws to version 3.3.2 (#4367) 2017-11-25 14:53:00 -05:00
greenkeeper[bot] 49d0a651b5 chore(package): update uws to version 9.14.0 (#4366) 2017-11-25 14:52:46 -05:00
Benjamin Wilson Friedman 7944e2bd2d Remove hidden properties from aggregate responses (#4351)
* Remove hidden properties from aggregrate responses

* transform results from mongo & postgres

* Adjust ordering to comply with tests
2017-11-22 23:07:45 -08:00
greenkeeper[bot] 37ceae0812 fix(package): update pg-promise to version 7.3.2 (#4362) 2017-11-18 21:12:52 +00:00
CHANG, TZU-YEN 8bf6abfee3 Patch handleShutdown feature (#4361)
* Add handleShutdown Test of MongoStorageAdapter.

* Add `handleShutdown` function in PostgresStorageAdapter, with it's test.

* Replace the _pgp to _client

* Use  `adapter._client.$pool.ending` to ckeck is ending

* Add `handleShutdown()` in ParseServer.spec.js

* Trigger

* Set Server to Default
2017-11-18 15:20:19 -05:00
Florent Vilmart de73f3723c Scoped packages (#4354)
* Updates for scoped packages dependencies

* bumps s3 adapter

* Update README.md

* Update README.md

* Update README.md
2017-11-17 08:32:58 -05:00
Benjamin Wilson Friedman e1d04cd655 Adds Table of Contents to README (#4357)
* adds table of contents

* remove empty line, moved greenkeeper up with other statuses
2017-11-16 18:49:11 -05:00
Diego Vieira aa1e97fd06 fix dead link to blog (#4353)
* fix dead link to blog

* point old blog to new domain
2017-11-15 10:06:33 -05:00
Florent Vilmart 09fee7d12b Adds optimization for related relations (#4345)
* Adds optimization for related relations

* Makes MongoStorageAdapter only able to sort on Join tables
2017-11-14 14:46:51 -05:00
Diamond Lewis 7223add446 Support for Aggregate Queries (#4207)
* Support for Aggregate Queries

* improve pg and coverage

* Mongo 3.4 aggregates and tests

* replace _id with objectId

* improve tests for objectId

* project with group query

* typo
2017-11-12 14:00:22 -05:00
Bryan de Leon 4e207d32a7 Fix for unhandled undefined config in reset password pages (#4334)
* Fix for unhandled undefined config

When an invalid application id is passed either for reset/change password or email verification, config.get returns undefined. This causes internal server.

* Throwing a 403 exception instead of returning a 404 for an invalid app id

Also, added a missing semicolon

* Fix indent issues

* Fix invalid colon to semicolon

* Fix space and indent issues

* Tests for the fix for unhandled undefined config
2017-11-11 09:42:20 -05:00
Florent Vilmart 72e20be06d fix #3451 duplicate session upon login (#4337)
* Adds failing test for #3451 (on multiple logins)

* Factor sessionDestruction as part of Session creation flow in RestWrite

* nits
2017-11-11 09:41:23 -05:00
Tom 932a474606 Remove hidden properties in handleLogin & handleMe (#4335) 2017-11-09 17:48:50 -08:00
greenkeeper[bot] 08ab1f453d fix(package): update parse to version 1.10.2 (#4333) 2017-11-08 19:23:56 -08:00
greenkeeper[bot] bb8041ee45 fix(package): update ws to version 3.3.1 (#4329) 2017-11-08 19:06:38 -05:00
Florent Vilmart a01159e22c fix(package): update pg-promise to version 7.3.1 (#4324)
Closes #4323
2017-11-06 10:50:50 -07:00
Emilie Montredon d163269c47 Updated README (#4325)
Added a link to the new SendinBlue adapter for parse-server
2017-11-05 20:59:15 -05:00
greenkeeper[bot] 0e16670b5a fix(package): update ws to version 3.3.0 (#4318) 2017-11-05 13:18:00 -05:00
Benjamin Wilson Friedman c1a7347143 Fix for _PushStatus Stuck 'running' when Count is Off (#4319)
* Fix for _PushStatus stuck 'running' if count is off

* use 'count' for batches

* push worker test fix
2017-11-05 13:04:46 -05:00
Worathiti Manosroi 842343a1a9 Add password confirmation to choose_password (#3994)
* update choose_password to have the confirmation

* update commander

* reverted the base

* remove some spaces

* nits

* more nits
2017-11-03 16:09:37 -04:00
greenkeeper[bot] a33c08a751 chore(package): update cross-env to version 5.1.1 (#4299) 2017-11-03 11:54:09 -04:00
Florent Vilmart 07df71a79b fix(package): update pg-promise to version 7.2.1 (#4316)
Closes #4303
2017-11-03 11:52:36 -04:00
greenkeeper[bot] cabcac221f fix(package): update pg-promise to version 7.1.0 (#4303) 2017-11-03 11:52:01 -04:00
Benjamin Wilson Friedman c0a81a88b7 Structured /health Response (#4305)
* modifies /health to return json instead of OK

* version removed!
2017-11-03 11:51:46 -04:00
Benjamin Wilson Friedman 46af1b6955 Adds Relative Time options now,weeks & years (#4304)
* Adds 'now' as an option in relative time

* reenables no-console in previous spot

* Adds weeks,months,years and abbreviations

* modified tests to address coverage

* month be gone!
2017-11-01 13:31:08 -07:00
Xavier Damman 84aadba23a Activating Open Collective (#3806)
* Added backers and sponsors on the README

* Added call to donate after npm install

* Update package.json
2017-10-26 21:26:05 -04:00
Florent Vilmart fecbb2d7ff ⚡ Release 2.6.5 (#4298) 2017-10-26 17:15:32 -04:00
marvelm 6f1fe89948 Relative time queries (#4289)
* Add relative time queries

* Encode successful result

* Add integration test

* Add more error cases

* Remove unnecessary new Date

* Error when time has both 'in' and 'ago'

* naturalTimeToDate -> relativeTimeToDate

* Add $relativeTime operator

* Throw error if $relativeTime is invalid

* Add integration test for invalid relative time

* Exclude $exists query

* Only run integration tests on MongoDB

* Add it_only_db test helper
https://github.com/parse-community/parse-server/blame/bd2ea87c1d508efe337a1e8880443b1a52a8fb81/CONTRIBUTING.md#L23

* Handle where val might be null or undefined

* Add integration test for multiple results

* Lowercase text before processing

* Always past if not future

* Precompute seconds multiplication

* Add shorthand for interval
hr, hrs
min, mins
sec, secs

* Throw error if $relativeTime is used with $exists, $ne, and $eq

* Improve coverage for relativeTimeToDate

* Add test for erroring on floating point units

* Remove unnecessary dropDatabase function

* Unit test $ne, $exists, $eq

* Verify field type

* Fix unit test for $exists
Unnest query object
2017-10-26 16:23:27 -04:00
Florent Vilmart 1dd58b7527 Adds support for read-only masterKey (#4297)
* Adds support for read-only masterKey

* Adds tests to make sure all endpoints are properly protected

* Updates readme

* nits
2017-10-26 15:35:07 -04:00
Benjamin Wilson Friedman 87b79cedfa Handle possible afterSave exception (#4293)
* capture and log exceptions caused by afterFind

* Wording

* Consolidated promise chaining

* use logger instead of console
2017-10-26 14:28:13 -04:00
adammlevy c2fc0f556e fix URL for Parse npm, was originally pointing to mongodb npm (#4295) 2017-10-26 11:45:33 -04:00
marvelm 8a23c00265 Add support for expiration interval in Push (#4202)
* Add support for expiration_interval in Push

* Support expiration_interval for immediate pushes

* Test

* Add 'expiration_interval' to _PushStatus class

* Fix coverage
2017-10-25 17:30:20 -04:00
Florent Vilmart a185c97557 The REST API key was improperly inferred from environment (#4292) 2017-10-25 11:46:40 -04:00
Florent Vilmart 3bb9e0e65e ⚡ Release 2.6.4 (#4287)
* ⚡ Release 2.6.4

* Marks port ENV var as PORT

* Fixes broken definitions

* Update CHANGELOG.md

* Update CHANGELOG.md

* Update CHANGELOG.md
2017-10-25 09:45:52 -04:00
Florent Vilmart 7d2332e651 Run nsp check on release (#4285)
* Run nsp check on release

* At the right place
2017-10-24 19:06:54 -04:00
Florent Vilmart 72888bc539 Adds ability to login with email when specifying it (#4276)
* Adds ability to login with email when specifying it

* Adds tests for corner cases

* nits
2017-10-24 17:50:48 -04:00
Steven Shipton 6685932b7f update dependencies (#4280) 2017-10-24 15:50:36 -04:00
Benjamin Wilson Friedman 0db858b04c Enables login over POST in addition to GET (#4268)
* Enables login over POST in addition to GET

* Removes explcit method:POST as rp.post does this naturally
2017-10-24 15:47:26 -04:00
Florent Vilmart d93b0e1fd9 Hotfix: cloud option should be a string 2017-10-23 09:11:08 -04:00
Florent Vilmart 9de4b8b2a7 Refactors configuration management (#4271)
* Adds flow types / Configuration interfaces

* Lets call it options

* Use a single interface to generate the configurations

* Translates options to definitions only if comments are set

* improves logic

* Moves objects around

* Fixes issue affecting logging of circular objects

* fixes undefined env

* Moves all defaults to defaults

* Adds back CLI defaults

* Restored defaults in commander.js

* Merge provided defaults and platform defaults

* Addresses visual nits

* Improves Config.js code

* Adds ability to pass the default value in trailing comments

* Load platform defaults from the definitions file

* proper default values on various options

* Adds ParseServer.start and server.start(options) as quick startup methods

* Moves creating liveQueryServer http into ParseServer.js

* removes dead code

* Adds tests to guarantee we can start a LQ Server from main module

* Fixes incorrect code regading liveQuery init port

* Start a http server for LQ if port is specified

* ensure we dont fail if config.port is not set

* Specify port

* ignore other path skipped in tests

* Adds test for custom middleware setting

* Refactors new Config into Config.get

- Hides AppCache from ParseServer.js, use Config.put which validates

* Extracts controller creation into Controllers/index.js

- This makes the ParseServer init way simpler

* Move serverURL inference into ParseServer

* review nits
2017-10-23 08:43:05 -04:00
greenkeeper[bot] d29a4483d0 fix(package): update parse-server-push-adapter to version 2.0.2 (#4286) 2017-10-22 23:03:00 -04:00
Florent Vilmart 44b17ef0d2 fix(package): update semver to version 5.4.1 (#4283)
Closes #4044
2017-10-22 15:46:19 -04:00
Florent Vilmart 484fdc2523 chore(package): update mongodb-runner to version 3.6.1 (#4284)
Closes #4164
2017-10-22 15:20:27 -04:00
Florent Vilmart 2b52c81d49 chore(package): update eslint-plugin-flowtype to version 2.39.1 (#4282) 2017-10-22 15:19:40 -04:00
Florent Vilmart d0e613a632 chore(package): update eslint to version 4.9.0 (#4281) 2017-10-22 14:40:50 -04:00
Florent Vilmart e4eeadd4c6 Bumps parse sdk 1.10.1 (#4274)
* Fail test case for Issue#3999

* no message

* Bumps Parse to 1.10.1

Updates Polygon test suite to leverage latest version
2017-10-21 16:03:24 -04:00
greenkeeper[bot] 31bd02ce5c fix(package): update parse-server-push-adapter to version 2.0.1 (#4275) 2017-10-21 14:23:10 -04:00
greenkeeper[bot] bc37972394 fix(package): update pg-promise to version 7.0.3 (#4270) 2017-10-21 12:14:04 -04:00
Gustav Ahlberg 70ad9e9ffd Add request ip to request object (#4265)
* add the client ip to the request config object

* add the config ip to the trigger request object

* add the config ip to the functions request object

* add tests

* remove log

* remove log
2017-10-18 08:13:09 -04:00
Benjamin Wilson Friedman 9376b4d04a Validate serverURL on Start (#4204)
* Added basic validation of publicServerURL

* Fixed up 'verifyServerUrl' and added tests

* Use Parse.serverURL instead, general cleanup.

* Test server port moved to 13376

* Removed reconfigureServer calls with simple changing of Parse.serverURL

* changed var to const

* Disabled automatic serverURL verification during testing, moved verification call into app.on('mount') callback, removed setTimeout from verification.
2017-10-17 14:49:28 -04:00
greenkeeper[bot] 9745caf9fb chore(package): update babel-preset-env to version 1.6.1 (#4263) 2017-10-17 14:48:23 -04:00
greenkeeper[bot] b3ea75b69a chore(package): update cross-env to version 5.1.0 (#4261) 2017-10-17 14:48:12 -04:00
Florent Vilmart 6963ff61b8 fix(package): update mongodb to version 2.2.33 (#4264)
Closes #4252
2017-10-17 14:48:01 -04:00
greenkeeper[bot] ac970a40c4 fix(package): update mongodb to version 2.2.32 (#4252) 2017-10-17 14:47:31 -04:00
Benjamin Wilson Friedman 315d30b426 Fix for beforeSave with increment causing key to be Dropped (#4259)
* Fixes an issue where a beforeSave hook could cause a numeric val to be dropped in response.

* Use hasOwnProperty to check instead

* Remove redundant set
2017-10-15 21:15:30 -07:00
greenkeeper[bot] 557a2b2827 fix(package): update pg-promise to version 7.0.2 (#4257) 2017-10-14 18:57:03 -04:00
Benjamin Wilson Friedman 9145c88562 Corrected outdated links (#4254) 2017-10-12 22:40:47 -04:00
Florent Vilmart f0b7ffcdd6 fix(package): update pg-promise to version 7.0.1 (#4248)
Closes #4239
2017-10-11 17:33:08 -04:00
Florent Vilmart 033437034d ⚡ Release 2.6.3 (#4233)
* ⚡ Release 2.6.3

* Update CHANGELOG.md
2017-10-03 14:33:05 -04:00
Florent Vilmart 7fac070435 Allow queries with String array for pointers containedIn (#4188)
* Adds basic failing test

* Adds ability to use [objectId] for $in/$nin pointers

* makes sure to use a set field

* Makes sure field is defined
2017-10-03 13:50:39 -04:00
Florent Vilmart 2a168936fc Adds support for pointer/string pointers comparison in LiveQuery (#4231)
* Adds support for pointer/string pointers comparison in LiveQuery

* nits

* Makes sure needed is set

* Update QueryTools.js

* Update QueryTools.js
2017-10-03 13:50:20 -04:00
Chris Norris 23bffc8883 Add maxLimit server configuration (#4048)
* Add maxLimit server configuration

* Fix maxlimit validation logic to correctly handle maxLimit:0 case
2017-10-02 09:23:09 -04:00
greenkeeper[bot] 976da4d715 fix(package): update winston to version 2.4.0 (#4227) 2017-10-02 09:18:34 -04:00
greenkeeper[bot] f2fc3ceabc fix(package): update pg-promise to version 6.10.2 (#4222) 2017-10-01 13:58:28 -04:00
Florent Vilmart c872da493d fix(package): update pg-promise to version 6.10.1 (#4221)
Closes #4213
2017-09-30 12:59:09 -04:00
bivisss 7c07fff7cc remove device token when APNS error is BadDeviceToken (#4220) 2017-09-29 16:05:45 -04:00
greenkeeper[bot] c777a263ba fix(package): update winston-daily-rotate-file to version 1.6.0 (#4157) 2017-09-28 10:08:25 -07:00
greenkeeper[bot] 61a3e3c7b6 fix(package): update request to version 2.83.0 (#4211) 2017-09-27 15:24:40 -07:00
Florent Vilmart 770aba193c fix(package): update pg-promise to version 6.9.0 (#4205)
* fix(package): update pg-promise to version 6.9.0

Closes #4160

* Update .travis.yml
2017-09-23 11:42:46 -04:00
greenkeeper[bot] a3890572c4 fix(package): update request to version 2.82.0 (#4189) 2017-09-23 10:31:40 -04:00
greenkeeper[bot] c059d15f56 fix(package): update body-parser to version 1.18.2 (#4201) 2017-09-23 10:31:20 -04:00
greenkeeper[bot] fdf19def48 chore(package): update request-promise to version 4.2.2 (#4200) 2017-09-23 10:31:05 -04:00
Tyler Brock 6eed955319 Export LRU cache on ParseServer Object (#4203) 2017-09-22 17:36:20 -07:00
Florent Vilmart b703ec8a79 Fix for #4194 - me no likey much (#4198)
* Adds failing test for #4194

* Use the rest module instad of the Parse._request

- Some users have an invalid serverUrl passed in the options

* removes console.logs, fixes tests that leveraged Parse._request
2017-09-22 12:05:06 -04:00
Tyler Brock 70ca7bdd41 Make some error messages more helpful (#4177) 2017-09-19 20:45:13 -07:00
Diamond Lewis 9371958a09 Postgres: Properly handle undefined field values (#4186)
Prevents `UnhandledPromiseRejectionWarning: Unhandled promise rejection` when building queries.
2017-09-19 07:59:48 -04:00
Anthony Mosca cf630ba462 Updating with two GeoPoints fails correctly. (#4162) 2017-09-19 07:12:40 -04:00
Florent Vilmart 406a21e967 chore(package): update eslint to version 4.7.1 (#4185) 2017-09-18 19:28:00 -04:00
Diamond Lewis 19bcfd27dd handle deleting of class that doesn't exist (#4184) 2017-09-18 19:02:56 -04:00
Florent Vilmart d8fd670013 ⚡ Release 2.6.2 (#4183) 2017-09-18 17:10:08 -04:00
marvelm 07ae85eef0 Prefix default push channel with applicationId (#4182) 2017-09-18 15:25:03 -04:00
Florent Vilmart a5ce9fc175 Refactor pushStatusHandler to use Parse instead of direct access (#4173)
* Refactors pushStatusHandler to use HTTP interface so we can bind CloudCode hooks

* Handle correctly nested dot atomic operations

* Better handling of restricted class names, add support for afterSave _PushStatus

* Adds simple testing for afterSave(PushStatus)

* Reverts jobStatusHandler

* Addresses fixes

* adds delays to all methods
2017-09-18 15:01:07 -04:00
Florent Vilmart a39d045c7d Fixes issue #4150: Session management (#4152)
* Adds tests

* Provide fix

* Fix post sessions (#4167)

* add tests

* provide fix

* remove console.log
2017-09-18 14:53:11 -04:00
Florent Vilmart ba0a51ddc9 Fixes unhandled promise rejection error while sending push (#4172) 2017-09-17 09:57:07 -04:00
Florent Vilmart 8d8a8b250e Fixes issue affecting liveQuery on location null/undefined values (#4171) 2017-09-17 11:57:11 +03:00
Florent Vilmart d0184f438d Live Query basic monitoring (#4168)
* Adds uuid based client identification to prevent overflows

* no-super

* Adds cloud code monitoring

* fixes test

* nit
2017-09-15 17:20:51 -04:00
greenkeeper[bot] 7ecb36e71e chore(package): update nodemon to version 1.12.1 (#4166) 2017-09-15 09:23:48 -04:00
greenkeeper[bot] be9f27493d fix(package): update ws to version 3.2.0 (#4165) 2017-09-15 08:10:18 -04:00
greenkeeper[bot] 64df33157d chore(package): update mongodb-runner to version 3.6.0 (#4164) 2017-09-15 08:09:35 -04:00
Florent Vilmart d598d73f36 Adds ability to track sent/failed PerUTCOffset in the PushWorker (#4158)
* Adds ability to track sent/failed PerUTCOffset in the PushWorker

- for scheduled push notifications at a certain time, it helps keep track of the state

* Makes sure we track it all correctly

* Adds to Postgres
2017-09-13 17:28:20 -04:00
greenkeeper[bot] be2760f9dd fix(package): update pg-promise to version 6.5.3 (#4154) 2017-09-13 09:32:26 -04:00
greenkeeper[bot] c121b9787e fix(package): update winston-daily-rotate-file to version 1.5.0 (#4151) 2017-09-12 18:26:08 -04:00
Florent Vilmart 3a17904ce8 Push: Cleanup invalid device tokens (#4149)
* Adds collecting invalid / expired device tokens from GCM / APNS

* Cleanup invalid installations based on responses from the adapters

* Adds test for cleanup

* Adds tests for removal
2017-09-12 14:53:05 -04:00
greenkeeper[bot] a1554d04ab fix(package): update body-parser to version 1.18.1 (#4148) 2017-09-12 14:19:03 -04:00
greenkeeper[bot] 956f1df0f0 chore(package): update babel-eslint to version 8.0.0 (#4147) 2017-09-12 14:18:26 -04:00
Anthony Mosca 4dce3bd63c Add support for more audience fields. (#4145)
* Add support for more audience fields.

* Only update audience when defined audience_id.
2017-09-11 22:06:21 -04:00
Florent Vilmart 9fbb5e29e8 ⚡ Release 2.6.1 (#4146)
* ⚡ Release 2.6.1

* Update CHANGELOG.md
2017-09-11 11:23:07 -04:00
Florent Vilmart a660a0c25f fix: Issue #4142 (#4144)
* Tweaks test in order to show the error

- Session is effectively created when it should not

* Do not create a session when users need verified accounts on signup
2017-09-11 11:07:39 -04:00
Florent Vilmart 839a117246 Fixes #2885 duplicate sessions (#4143)
* Adds test to repro the issue

* Improved test

* Destroy duplicate sessions for User/Installation-id pair

- Sessions will also be created with action login instead of signup when using 3rd party auth
2017-09-11 09:52:18 -04:00
marvelm bc3cef2cd9 Support local time for scheduled pushes (#4137)
* Handle local push time

* PR feedback

* Improve timezone detection with regex

* Use indexOf instead of endsWith

* Add documentation

* Add end to end test for scheduled pushes in local time

* Revert changes to npm-git script

* clean up
2017-09-11 09:31:46 -04:00
greenkeeper[bot] 21f4411571 fix(package): update pg-promise to version 6.3.2 (#4009) 2017-09-09 14:02:54 -04:00
greenkeeper[bot] 0fb16a7848 chore(package): update babel-cli to version 6.26.0 (#4098) 2017-09-09 14:02:36 -04:00
Florent Vilmart 0f840b6bb8 Fix/issue pointer permissions (#4141)
* Makes sure we don't override roles

* Reduces the query size whith pointer permissions

- Does not return as $and if not needed
- Returns just the query with the additional constraint

* Do not use $in if include is just of length 1
2017-09-09 14:02:07 -04:00
Antonio Davi Macedo Coelho de Castro 019f9e55e1 Fix(MongoTransform): Ignore Audience legacy fields (#4018) 2017-09-09 13:27:54 -04:00
Jeremy May 121d151af9 Add master key override to live query ACL checks (#4133)
* Add master key override to live query ACL checks

* Fix mockClient so masterKey tests work correctly
2017-09-09 13:26:18 -04:00
Worathiti Manosroi 52c4dd3704 Refactor masterKeyIps and update readme (#4050)
* update choose_password to have the confirmation

* just a revert to the old one

* refactor and update readme

* just revert some spaces
2017-09-09 13:24:43 -04:00
Stephen Tuso 952e64d94a Update FilesAdapter comments (#4065) 2017-09-09 13:11:16 -04:00
greenkeeper[bot] 1914083942 fix(package): update body-parser to version 1.18.0 (#4139) 2017-09-09 13:09:46 -04:00
Florent Vilmart 139b9e1cb3 Finding areas that are untested and need love (#4131)
* Makes InstallationRouter like others

* Adds testing for Range file requests

- Fixes issue with small requests (0-2)

* Revert "Makes InstallationRouter like others"

This reverts commit e2d2a16ebf2757db6138c7b5b33c97c56c69ead6.

* Better handling of errors in FilesRouter

* Fix incorrectness in range requests

* Better/simpler logic

* Only on mongo at it requires Gridstore

* Open file streaming to all adapters supporting it

* Improves coverage of parsers

* Ensures depreciation warning is effective

* Removes unused function

* de-duplicate logic

* Removes necessity of overriding req.params.className on subclasses routers

* Use babel-preset-env to ensure min-version compatible code

* removes dead code

* Leverage indexes in order to infer which field is duplicated upon signup

- A note mentioned that it would be possible to leverage using the indexes on username/email to infer which is duplicated

* Small nit

* Better template to match column name

* Restores original implementation for safety

* nits
2017-09-05 17:51:11 -04:00
Florent Vilmart 3079270b3e Optimizations (#4135)
* removes costly json serialization to InMemoryCacheAdapter

* Always cache a copy of the array

* Use own mapValues

* Makes sure we dont make unnecessary calls to the logger

* Do not bypass loggers with silent logging (only applies to stdout)

* warn is not warning

* use ===

* Wrap logRequest / logResponse in the loggerController for more granular control

Also give the ability to pass functions to the logger so we don't serialize too early in JSON (costly)

* reconfiguring winston would override the transports levels and make subsequent tests fail
2017-09-04 20:47:49 -04:00
greenkeeper[bot] 17f4dcd176 chore(package): update nodemon to version 1.11.1 (#4134) 2017-09-04 15:17:33 -07:00
Diamond Lewis 5aafc93476 Text Index Support (#4081)
* add text index support

* additional validation

* multiple text index error

* rename function

* Delete text indexes message
2017-09-02 12:31:19 -04:00
Florent Vilmart 0bace67bb1 Adds push localization feature flag on serverInfo (#4130) 2017-09-01 18:08:02 -04:00
Florent Vilmart 6df944704c Adds support for localized push notification in push payload (#4129)
* Adds support for localized push data keys

- passign alert-[lang|locale] or title-[lang|locale] will inject the
  proper locale on the push body based on the installation

* Better handling of the default cases

* Updates changelog

* nits

* nits
2017-09-01 15:22:02 -04:00
Florent Vilmart 540daa4c4a ⚡ Release 2.6.0 (#4126) 2017-08-30 14:08:20 -04:00
Florent Vilmart 2e1ba66fd2 Fix/user update issue (#4123)
* Adds failing test,

the _User object is not updated as soon as you pass some authData part of the PUT

* Do not run the DB call when updating the user with new auth data, just part of the rest
2017-08-29 16:34:59 -04:00
Arthur Cinader a103871cbc fix some typos. (#4112)
* fix some typos.

* more typos....
2017-08-29 14:20:51 -04:00
Arthur Cinader f8403943ab Greenkeeper/parse server s3 adapter 1.2.0 (#4118)
* fix(package): update parse-server-s3-adapter to version 1.2.0

* Add changelog note to explain the adapter change.
2017-08-29 14:20:25 -04:00
greenkeeper[bot] da2bfca618 fix(package): update ws to version 3.1.0 (#4053) 2017-08-29 10:08:25 -07:00
greenkeeper[bot] 1798c07874 fix(package): update mime to version 1.4.0 (#4120) 2017-08-29 09:46:10 -07:00
greenkeeper[bot] e2d1fd3fe0 fix(package): update semver to version 5.4.0 (#4044) 2017-08-29 09:14:54 -07:00
Florent Vilmart ea67d23ef4 Improvements for sending push performance (#4122)
* Adds test for stalled pushStatus when audience is empty

* fixup! Adds test for stalled pushStatus when audience is empty

* Do not enqueue when count is 0, enforce deviceToken exists, stop badge ordering
2017-08-29 11:47:01 -04:00
greenkeeper[bot] cfd7bc0f3b chore(package): update uws to version 8.14.1 (#4078) 2017-08-28 10:17:13 -07:00
greenkeeper[bot] c4d921e3eb chore(package): update jasmine to version 2.8.0 (#4115) 2017-08-24 12:49:46 -07:00
greenkeeper[bot] 8513b697e9 chore(package): update bcrypt to version 1.0.3 (#4113) 2017-08-24 10:15:23 -04:00
Arthur Cinader ff4ae55e34 Fix minor comment typo (#4110)
* Fix minor comment typo

* get the rest of the typos.  use US spelling for behavior.
2017-08-23 10:41:27 -07:00
greenkeeper[bot] 5d359c3b63 chore(package): update babel-register to version 6.26.0 (#4097) 2017-08-23 11:35:52 -04:00
greenkeeper[bot] 6e23822f99 chore(package): update babel-core to version 6.26.0 (#4096) 2017-08-23 11:35:40 -04:00
greenkeeper[bot] b45f308ff9 fix(package): update mongodb to version 2.2.31 (#4089) 2017-08-23 11:35:23 -04:00
greenkeeper[bot] 3ed6dd2685 fix(package): update redis to version 2.8.0 (#4087) 2017-08-23 11:35:08 -04:00
Vitaly Tomilov 2b4c7570ea Update PostgresStorageAdapter.js (#4094)
* proper use of the connections
* proper integrity: creating tables within a single transaction.
2017-08-23 11:34:57 -04:00
Diamond Lewis c6546218f4 PG: Support for nested contains and containedIn (#4109) 2017-08-23 11:33:57 -04:00
Florent Vilmart c2c059f625 Smaller build matrix (#3986) 2017-08-11 13:17:58 -04:00
Arthur Cinader 008cb1dbfe Fix nit whitespace issue that is breaking lint. (#4092)
* Not sure how this got introduced without lint catching either in the upgrade of a lib or a pr on these files, but innocuous enough that i don't think we need to investigate further?

* Update eslint to make all happy!
2017-08-10 23:31:32 +03:00
greenkeeper[bot] 2cec92007a chore(package): update jasmine to version 2.7.0 (#4061) 2017-08-10 11:18:36 -04:00
greenkeeper[bot] 47f00e606f chore(package): update cross-env to version 5.0.2 (#4071) 2017-08-10 08:42:51 -04:00
Florent Vilmart 92d51def6b Fixes issue affecting linking users to a 3rd party auth (#4047)
* Fixes issue affecting linking users to a 3rd party auth

* Fixes problematic test

* Better fix

* nits
2017-07-25 15:26:34 -04:00
Worathiti Manosroi 7e54265f6d Security: limit Masterkey remote access (#4017)
* update choose_password to have the confirmation

* add comment mark

* First version, no test

* throw error right away instead of just use masterKey false

* fix the logic

* move it up before the masterKey check

* adding some test

* typo

* remove the choose_password

* newline

* add cli options

* remove trailing space

* handle  in case the server is behind proxy

* add getting the first ip in the ip list of xff

* sanity check the ip in config if it is a valid ip address

* split ip extraction to another function

* trailing spaces
2017-07-23 12:26:30 -04:00
Diamond Lewis 811d8b0c7a pg support for null in containedAll (#4026) 2017-07-23 12:11:02 -04:00
Miguel Serrano 3c79cae1b2 feat: add request headers to trigger functions (#4012)
* add request headers to trigger functions

* reverse changes

* add headers in request config and trigger request
2017-07-14 13:19:00 -04:00
Diamond Lewis e6cc8204b3 Add Polygon Type To Schema / PolygonContain to Query (#3944)
* Added type polygon to schema

* refactoring and more tests

* fix tests

* update test and transform

* add support for polygonContains

* fix transform.mongoObjectToParseObject test

* add indexes for polygon

* index test

* postgres test fix

* remove invalid loop test

* add invalid loop test

* nit
2017-07-11 23:33:45 -04:00
greenkeeper[bot] 0571c6f95e fix(package): update mongodb to version 2.2.30 (#4003) 2017-07-08 14:18:24 -04:00
greenkeeper[bot] 58079f00f9 fix(package): update pg-promise to version 6.3.1 (#3992) 2017-07-08 14:18:10 -04:00
RiverAge a26e4c7470 fix: proper error message when login with wechat fails (#4000)
* fix typo

it should be weixin not qq.

* change error message from weixin to wechat

* remove unnecessary check
2017-07-08 08:29:02 -04:00
greenkeeper[bot] 9d6b4f4cc4 fix(package): update commander to version 2.11.0 (#3988) 2017-07-03 21:27:21 -04:00
Florent Vilmart d158023083 ⚡ Release 2.5.3 (#3989) 2017-07-03 17:50:04 +03:00
Florent Vilmart ad52ed64af Lets increment/set badges on all _Installations (#3970)
* Lets increment/set badges on all _Installations

* Makes sure PG update where query is properly formed with empty query
2017-07-03 10:16:04 -04:00
Florent Vilmart 5931aa8851 ⚡ Release 2.5.2 (#3985)
* Adds ability to configure cache from cli

* dont use array.includes for node 4.6

* Changelog and version bump

* Removes runtime check for version

* Build releases on 4.6
2017-07-02 20:50:13 -04:00
rihadavid 287810443b Include username in password reset email (#3969)
When user resets his password, he does it by filling his email. But often he could also not remember his username, which he needs for logging in. I think it's better to include the username in the password reset email, what do you think?
2017-07-02 16:20:25 -04:00
289 changed files with 69320 additions and 30863 deletions
+9 -4
View File
@@ -1,9 +1,14 @@
{
"plugins": [
"transform-flow-strip-types"
"@babel/plugin-transform-flow-strip-types",
"@babel/plugin-proposal-object-rest-spread"
],
"presets": [
"es2015",
"stage-0"
]
["@babel/preset-env", {
"targets": {
"node": "8"
}
}]
],
"sourceMaps": "inline"
}
+1 -1
View File
@@ -1,3 +1,3 @@
lib
coverage
out
+1 -1
View File
@@ -14,7 +14,7 @@
"sourceType": "module"
},
"rules": {
"indent": ["error", 2],
"indent": ["error", 2, { "SwitchCase": 1 }],
"linebreak-style": ["error", "unix"],
"no-trailing-spaces": 2,
"eol-last": 2,
+1
View File
@@ -7,3 +7,4 @@
[libs]
[options]
suppress_comment= \\(.\\|\n\\)*\\@flow-disable-next
+15
View File
@@ -0,0 +1,15 @@
* text=auto eol=lf
*.js text
*.html text
*.less text
*.json text
*.css text
*.xml text
*.md text
*.txt text
*.yml text
*.sql text
*.sh text
*.png binary
+11 -9
View File
@@ -1,28 +1,30 @@
<!--
**Please use this template. If you don't use this template, your issue may be closed without comment.**
We use GitHub Issues for bugs.
If you have a non-bug question, ask on Stack Overflow or Server Fault:
- https://stackoverflow.com/questions/tagged/parse.com
- https://serverfault.com/tags/parse
If you have a non-bug question, ask on [Parse Community's Discourse forum](https://community.parseplatform.org/c/parse-server)
You may also search through existing issues before opening a new one: https://github.com/ParsePlatform/Parse-Server/issues?utf8=%E2%9C%93&q=is%3Aissue
If you have a vulnerability disclosure, please follow our policy available here https://github.com/parse-community/parse-server/blob/master/SECURITY.md
--- Please use this template. If you don't use this template, your issue may be closed without comment. ---
You may also search through existing issues before opening a new one: https://github.com/parse-community/parse-server/issues?utf8=%E2%9C%93&q=is%3Aissue
-->
### Issue Description
Describe your issue in as much detail as possible.
<!-- Describe your issue in as much detail as possible. -->
### Steps to reproduce
Please include a detailed list of steps that reproduce the issue. Include curl commands when applicable.
<!-- Please include a detailed list of steps that reproduce the issue. Include curl commands when applicable. -->
#### Expected Results
What you expected to happen.
<!-- What you expected to happen. -->
#### Actual Outcome
What is happening instead.
<!-- What is happening instead. -->
### Environment Setup
@@ -0,0 +1,17 @@
---
name: "\U0001F4A1 Feature request"
about: Suggest an idea for this project
---
**Is your feature request related to a problem? Please describe.**
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
**Describe the solution you'd like**
A clear and concise description of what you want to happen.
**Describe alternatives you've considered**
A clear and concise description of any alternative solutions or features you've considered.
**Additional context**
Add any other context or screenshots about the feature request here.
@@ -0,0 +1,5 @@
---
name: "🙋‍Getting Help"
about: Join https://community.parseplatform.org
---
@@ -0,0 +1,56 @@
---
name: "\U0001F525 parse-server 3.0.0"
about: Report an issue while migrating to parse-server 3.0.0
---
<!---
** Use this template only if you have an issue migrating to parse-server 3.0.0 **
If you have a question, ask on [Parse Community's Discourse forum](https://community.parseplatform.org/c/parse-server)
If you have a vulnerability disclosure, please follow our policy available here https://github.com/parse-community/parse-server/blob/master/SECURITY.md
You may also search through existing issues before opening a new one: https://github.com/parse-community/parse-server/issues?utf8=%E2%9C%93&q=is%3Aissue
--- Please use this template. If you don't use this template, your issue may be closed without comment. ---
--->
# Before opening the issue please ensure that you have:
- [ ] [Read the migration guide](https://github.com/parse-community/parse-server/blob/master/3.0.0.md) to parse-server 3.0.0
- [ ] [Read the migration guide](https://github.com/parse-community/Parse-SDK-JS/blob/master/2.0.0.md) to Parse SDK JS 2.0.0
### Issue Description
<!--- Describe your issue in as much detail as possible. -->
### Steps to reproduce
<!--- Please include a detailed list of steps that reproduce the issue. Include curl commands when applicable. --->
### Expected Results
<!--- What you expected to happen. --->
### Actual Outcome
<!--- What is happening instead. --->
### Environment Setup
- **Server**
- parse-server version (Be specific! Don't say 'latest'.) : [FILL THIS OUT]
- Operating System: [FILL THIS OUT]
- Hardware: [FILL THIS OUT]
- Localhost or remote server? (AWS, Heroku, Azure, Digital Ocean, etc): [FILL THIS OUT]
- **Database**
- MongoDB version: [FILL THIS OUT]
- Storage engine: [FILL THIS OUT]
- Hardware: [FILL THIS OUT]
- Localhost or remote server? (AWS, mLab, ObjectRocket, Digital Ocean, etc): [FILL THIS OUT]
### Logs/Trace
<!--- Include all relevant logs. You can turn on additional logging by configuring VERBOSE=1 in your environment. --->
@@ -0,0 +1,49 @@
---
name: "\U0001F4F2 Push Notifications"
about: Issues with setting up or delivering push notifications
---
<!---
**We use GitHub Issues for reporting bugs with parse-server.**
If you have a *question*, you should join the [Parse Community's Discourse forum](https://community.parseplatform.org/c/parse-server)
If you have a non-bug question, ask on Stack Overflow or Server Fault:
- https://stackoverflow.com/questions/tagged/parse.com
- https://serverfault.com/tags/parse
If you have a vulnerability disclosure, please follow our policy available here https://github.com/parse-community/parse-server/blob/master/SECURITY.md
You may also search through existing issues before opening a new one: https://github.com/parse-community/parse-server/issues?utf8=%E2%9C%93&q=is%3Aissue
--- Please use this template. If you don't use this template, your issue may be closed without comment. ---
--->
### Issue Description
<!--- Describe your issue in as much detail as possible. -->
### Push Configuration
Please provide a copy of your `push` configuration here, obfuscating any sensitive part.
### Environment Setup
- **Server**
- parse-server version (Be specific! Don't say 'latest'.) : [FILL THIS OUT]
- Operating System: [FILL THIS OUT]
- Hardware: [FILL THIS OUT]
- Localhost or remote server? (AWS, Heroku, Azure, Digital Ocean, etc): [FILL THIS OUT]
- **Database**
- MongoDB version: [FILL THIS OUT]
- Storage engine: [FILL THIS OUT]
- Hardware: [FILL THIS OUT]
- Localhost or remote server? (AWS, mLab, ObjectRocket, Digital Ocean, etc): [FILL THIS OUT]
### Logs/Trace
<!--- Include all relevant logs. You can turn on additional logging by configuring VERBOSE=1 in your environment. --->
@@ -0,0 +1,51 @@
---
name: "\U0001F41B Report an issue"
about: Report an issue on parse-server
---
<!---
**We use GitHub Issues for reporting bugs with parse-server.**
If you have a *question*, you should join the [Parse Community's Discourse forum](https://community.parseplatform.org/c/parse-server).
If you have a vulnerability disclosure, please follow our policy available here https://github.com/parse-community/parse-server/blob/master/SECURITY.md
You may also search through existing issues before opening a new one: https://github.com/parse-community/parse-server/issues?utf8=%E2%9C%93&q=is%3Aissue
--- Please use this template. If you don't use this template, your issue may be closed without comment. ---
--->
### Issue Description
<!--- Describe your issue in as much detail as possible. -->
### Steps to reproduce
<!--- Please include a detailed list of steps that reproduce the issue. Include curl commands when applicable. --->
### Expected Results
<!--- What you expected to happen. --->
### Actual Outcome
<!--- What is happening instead. --->
### Environment Setup
- **Server**
- parse-server version (Be specific! Don't say 'latest'.) : [FILL THIS OUT]
- Operating System: [FILL THIS OUT]
- Hardware: [FILL THIS OUT]
- Localhost or remote server? (AWS, Heroku, Azure, Digital Ocean, etc): [FILL THIS OUT]
- **Database**
- MongoDB version: [FILL THIS OUT]
- Storage engine: [FILL THIS OUT]
- Hardware: [FILL THIS OUT]
- Localhost or remote server? (AWS, mLab, ObjectRocket, Digital Ocean, etc): [FILL THIS OUT]
### Logs/Trace
<!--- Include all relevant logs. You can turn on additional logging by configuring VERBOSE=1 in your environment. --->
+24
View File
@@ -0,0 +1,24 @@
# Number of days of inactivity before an issue becomes stale
daysUntilStale: 45
# Number of days of inactivity before a stale issue is closed
daysUntilClose: 7
# Issues with these labels will never be considered stale
exemptLabels:
- pinned
- security
- good first issue
- up-for-grabs
- bug
- enhancement
- help wanted
# Label to use when marking an issue as stale
staleLabel: wontfix
# Limit to only `issues` not `pulls`
only: issues
# Comment to post when marking an issue as stale. Set to `false` to disable
markComment: >
This issue has been automatically marked as stale because it has not had
recent activity. It will be closed if no further activity occurs. Thank you
for your contributions.
# Comment to post when closing a stale issue. Set to `false` to disable
closeComment: false
+4
View File
@@ -15,6 +15,10 @@ lib-cov
coverage
.nyc_output
# docs output
out
docs
# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
.grunt
+2
View File
@@ -0,0 +1,2 @@
10.14.2
+1 -2
View File
@@ -4,8 +4,7 @@
"text-summary"
],
"exclude": [
"**/spec/**",
"lib/"
"**/spec/**"
]
}
+3
View File
@@ -0,0 +1,3 @@
semi: true
trailingComma: "es5"
singleQuote: true
+51 -40
View File
@@ -1,68 +1,79 @@
language: node_js
dist: trusty
node_js:
- '6.10'
- '7.10'
services:
- mongodb
- postgresql
- redis-server
- docker
- mongodb
- postgresql
- redis-server
- docker
addons:
postgresql: '9.5'
apt_packages:
- postgresql-9.5-postgis-2.3
apt:
packages:
- postgresql-9.5-postgis-2.3
branches:
only:
- master
- /^[0-9]+.[0-9]+.[0-9]+(-.*)?$/
- /^greenkeeper/.*$/
- "/^[0-9]+.[0-9]+.[0-9]+(-.*)?$/"
- 3.x
- "/^greenkeeper/.*$/"
cache:
directories:
- node_modules
- "$HOME/.npm"
- "$HOME/.mongodb/versions"
# Test stage
stage: test
env:
global:
- COVERAGE_OPTION='./node_modules/.bin/nyc'
matrix:
- MONGODB_VERSION=3.2.13
- MONGODB_VERSION=3.4.4
- MONGODB_VERSION=4.0.4
- MONGODB_VERSION=3.6.9
- PARSE_SERVER_TEST_DB=postgres
- PARSE_SERVER_TEST_CACHE=redis
- NODE_VERSION=11.4.0
matrix:
allow_failures:
- env: NODE_VERSION=11.4.0
before_install:
- nvm install $NODE_VERSION
- nvm use $NODE_VERSION
- npm install -g greenkeeper-lockfile@1
before_script:
- node -e 'require("./lib/index.js")'
- psql -c 'create database parse_server_postgres_adapter_test_database;' -U postgres
- psql -c 'CREATE EXTENSION postgis;' -U postgres -d parse_server_postgres_adapter_test_database
- psql -c 'CREATE EXTENSION postgis_topology;' -U postgres -d parse_server_postgres_adapter_test_database
- silent=1 mongodb-runner --start
- greenkeeper-lockfile-update
script:
- npm run lint
- npm run coverage
after_script:
- greenkeeper-lockfile-upload
- bash <(curl -s https://codecov.io/bash)
jobs:
include:
# release on github latest branch
- stage: release
node_js: '6.10'
env:
before_script: skip
after_script: skip
script: skip
deploy:
- provider: script
skip_cleanup: true
script: ./resources/npm-git.sh
on:
branch: master
- provider: npm
skip_cleanup: true
email:
secure: "YweTGc22uqFWpzbfiUa5ptBLRAy6tt6d9TZLwEkPtmnsWzN9dguGyKWmXiw0qL+848FWQ5PWzUgBn5XdigV9tF3rJY6RGs8i38WulNjwSoGuRZa3AChsQHAb1KenANcJybzhnwgEj9gRsrGZPEsyI2whfake/xLDtG91kHjpJANsd4gseOh6fdS4FIYCbyXvSbC7S0yZzIJkgSkMqJO7RJ8r0HYQ7srYIw31dM3ZXSmUYu+GaMDUUu3RLAGYoKaROxhDRnzkjdeLLiwZH8hQ/6CaqwqX54iJ6OS+MOQU3fi5ZXalA7RZvtC4RmrcCkaTf3i0f+5xejYIFgzXdYGMVm8DUc82tDw1s4b6Pb19bgi1xXOQ0IKzRmZuGxnvkRN61dnYdnpnnNuG97HXgVjiOigZXVLZkWazUdnf9zXqmkC+KxPfa4Ldqg0TMjQ9J14n6TXxRti8Tt0xMa1Uzho7VdsxdJy35Bghy398O6X8VdX6parfzEkX7c/JzcA3TIMJ9+S1dy3J4Tb2URB5367W6h7cDeXtGmwLTFu75Q1CNqRJkUGbSrX2NyMqG5tc8oaTM+OWvLxcbKPRy9T6qN7x2JmCHHaapf8/8VR9wCy2PwE5j+KWhmctEHqqOgrtG5gsjC0eCPJsposxZWyM7M6aUpXe3w+olBfKq9apUGStUSU="
api_key:
secure: "QprkaqQ+WCvZQR4qIEs5iS6peMCbRd8Hgt0s6HfdmhavNXwDFY8Bkdf6zJwWHLiqs4pyClXDZ2f6QiOs7y9IvJZ+wOIbsf4N5V6s06zOxJ0NAOwhe0mgWS3Us0zgXIfmW4BpmGnU4ql/qGL+9vNfyQJ7wxEJxVK7hiYh9Epu49E2jmefDqTX+SNSrDCg4HkRcxerxYGnAJDCP50QaNlyLSciODD6wHaddrSYkdvmISLMnLHug61OkE4OBIOWXfYV+e31kDj+zgczAfVQgekDKtaimCQclHFrmaEPl0KIm6wsDQAw5HWkepA/WZfv4SbCrDaKJDZw+LBI7dR0ezmiOH/zmWWrRW7D4wjkDGiumWjA8etuf8I4GRyC/d1RS+hnlvPr0Bu+WljuVxLoK3nhZOdiK5t4QlVDoGankkRjLylwFQgo8tzu5N0dc26z3ClowTwcKsjStmFr53gjCD7l3qoFjyPot1JlW3LPhG9Nch7rK33/7ONqVai3zxb1xB9ynd8TSzKi4/66LeYEDcGVM5A9Vmkp+egCnOhkOAXdI8O4jid98NANir+U4xmUYZ2PAMNmSeHlpSpwH2pC1/BHpDKA9RZEuEdr3sgmtuXXwwOCp+xvuVwxZZ6+gVEjG4nGTqSNxUIq1fzjGih8ElJtbM1Uhh2dVE9uxW7EC/oCuuk="
on:
tags: true
all_branches: true
repo: parse-community/parse-server
- stage: release
node_js: '10'
env:
before_script: skip
after_script: skip
script:
- "./release_docs.sh"
deploy:
- provider: pages
skip_cleanup: true
github_token:
secure: YU2lUqmW036AHBRu7xO/AwEeQ900Q/5O6FL96ZqWEfD7Gadaq4iapkNvhPR0HcZYRHqQV2/2LCHVnhd0dbj0ShkmVIHdQE7O+MF+j0v0GIVc8FPTPe1/d2Hy4apSWNe6FeCrYKVeliAu+ZqvNuFLhVmYvIeJdlJrMGOb6P76UZXRDv2srXhq4uBcCVUJuTajyyd5ttJfcNapymTP+xzEDYc7Hr4LJaubmv/wVD/xwPBbvfYFuBqysUpkPKi/ODlQbB0ybYh2fFnX71WUyFUGbtB5xI9ma951Zp4v3t73c31uUl27dJaHzO62EtVTdcUKAa804EtAtsvpeJWMVWKUgigm9UZcXdEwKa79fl5nLaq34lrSktAYOkexwPqYj+vbS6sn52JrluSxLE+4cEke7tYbnJ9X12SAQXKgcXY3n30+6gKf9RVqYdlNsYpEAqKVIDb6SlEkk86dP+uIg/XXb5RKEwxbDXnb6xdl9JRc+GTbeeY3/vg2h9QTdmFVblPtBhHrNenQYP/BS0n+EfUnAIBKqRmQgyhao3SiY5FMACM8higI2Lvvhpq46pDhXqsexYCz5F008C6YXGDh5gC93rJFec0pjh55DNdQu6uw3YMQ5jtf3QUXoPAoMFud3cTulMlnjC1WZG8QbqER8dzUZ4TcaQUdzribJI/mRriheBg=
local_dir: docs/
on:
all_branches: true
- provider: npm
api_key:
secure: Yrng6jsnMAtzrrln9DwRuY4xpcxl/WYS/1A5fckyQF6DsLmNlvqVtu3MWF+zdJgANF63G3jIee11tNBpYRecHl8FjPGwO0kc1vEgvIRVnveyR0bwIIDH6s/mR9dg4rZikflwG4XExsLyaQd7ko8aTIOIayfxJiv/u0yqwuBuBW2bFrL/41b1cKGK5+Iq2a+PdFENUPenKXISkACGaMnQF4Y/KVF98UwCiGLf957yFWc2sD6TFbjNDbAENSccvg1J3fBb+djbtzKzldl29ntp2mKVGSVASiKCRa6hSfgQulHiidFqFIKgpYJ1uATRr3UFr/NRVt1WbrgLnzY74OCX7y02c/xiYQMMEPRl/P8hHJu+KjQ3PBWsBvmsRN0QMUJv3PEjUPE3AY8sw49NoxiJZzJr7574vUBuM/dk0byYI6K/gwmUrPIhQjljzZqinS8JJJ4FjGjCdzXRhT5Q+PZvt5bF1rMOZXayNJZa+cICtmiJoU3vO2Yf6TXC3wY1veKvmcs24nws5lp4keJePTKAi1Ig7VoSxwAlgs3EGANIh7oBKx9zWnXQYMdmxbYsvLXAWcnnM+PcSCvooLmXWso3BQBeRuUUSS2oLaBpsFMsiniLNbA1cW4fwMkgUGz4oEDRi8wTD89E/1J2oNxzqtWRPawcVKpMpnBbDJHrWJPEHMw=
email:
secure: lomzDl71N995SzRczm7VE9OZE+PzMo4X8t3zU97agu/FMH5Qcj8BLwE+uVDTnA9Vblyj62ZsFKsNjP2Qp53Vcd+jHM4EJNWNRZYpEMIRO3LngX43r83qoFEHUvPu9s1oaa04a6FojcsJx0wl6B6Ke2AX74MXnJDLb9iZBy1mkpLUMVccVhSfhdoIzhkq3dhUw+6d8C024tNMHcgDW3VnRAsWFtiL7dCMpjLOdI+UxlkeGkQkxXXuRsZ0ZdjoSoM8NSkiYMc8x6EnekyRDoHTujX3OFxuU6+GAjrUmVzNmJWrBIqHVb0DXBQxjEaG3d/cNu5UsQyZYq5sxRRH0BaLs7F4oIQg95etasEtTtUkmsZ3pshVlsweiLU366UdbfuAf5hrJjqLrU12BKZyLjaAwyeKz031r8dA4sJtGIp5uVdXobQQTH6r958A88byJ20uaYSqhqjhZo3hkWXIQP0WQN2Ej/g57HbVNLB/nPKkMILfk/tpp7nBDLT0QrjbZxeo1dwCHqsBEV6z7ZyWyFf4xwpDsir4txL4t8ElzeGdlACjCqAJvIh5w9YzfrwijtoVMvvP6pWvn/iI640d4rsdIDe8egxgqZ1R/TMd/tdHYX+eI+ZfFmCVGj36/uXwdG7KIoIZVjRQ2tvWr9ZuydEPWPSRVGT4ycFeu6wbm3elM3I=
skip_cleanup: true
on:
tags: true
all_branches: true
repo: parse-community/parse-server
+226
View File
@@ -0,0 +1,226 @@
# Upgrading Parse Server to version 3.0.0
parse-server 3.0.0 comes also with the JS SDK version 2.0 which requires a migration. Follow the migration guide [here](https://github.com/parse-community/Parse-SDK-JS/blob/master/2.0.0.md).
With the 3.0.0, parse-server has completely revamped it's cloud code interface. Gone are the backbone style calls, welcome promises and async/await.
In order to leverage those new nodejs features, you'll need to run at least node 8.10. We've dropped the support of node 6 a few months ago, so if you haven't made the change yet, now would be a really good time to take the plunge.
## Migrating Cloud Code Hooks
### Synchronous validations:
```js
// before
Parse.Cloud.beforeSave('MyClassName', function(request, response) {
if (!passesValidation(request.object)) {
response.error('Ooops something went wrong');
} else {
response.success();
}
});
// after
Parse.Cloud.beforeSave('MyClassName', (request) => {
if (!passesValidation(request.object)) {
throw 'Ooops something went wrong';
}
});
```
All methods are wrapped in promises, so you can freely `throw` `Error`, `Parse.Error` or `string` to mark an error.
### Asynchronous validations:
For asynchronous code, you can use promises or async / await.
Consider the following beforeSave call that would replace the contents of a fileURL with a proper copy of the image as a Parse.File.
```js
// before
Parse.Cloud.beforeSave('Post', function(request, response) {
Parse.Cloud.httpRequest({
url: request.object.get('fileURL'),
success: function(contents) {
const file = new Parse.File('image.png', { base64: contents.buffer.toString('base64') });
file.save({
success: function() {
request.object.set('file', file);
response.success();
},
error: response.error
});
},
error: response.error
});
});
```
As we can see the current way, with backbone style callbacks is quite tough to read and maintain.
It's also not really trivial to handle errors, as you need to pass the response.error to each error handlers.
Now it can be modernized with promises:
```js
// after (with promises)
Parse.Cloud.beforeSave('MyClassName', (request) => {
return Parse.Cloud.httpRequest({
url: request.object.get('fileURL')
}).then((contents) => {
const file = new Parse.File('image.png', { base64: contents.buffer.toString('base64') });
request.object.set('file', file);
return file.save();
});
});
```
And you can even make it better with async/await.
```js
// after with async/await
Parse.Cloud.beforeSave('MyClassName', async (request) => {
const contents = await Parse.Cloud.httpRequest({
url: request.object.get('fileURL')
});
const file = new Parse.File('image.png', { base64: contents.buffer.toString('base64') });
request.object.set('file', file);
await file.save();
});
```
## Aborting hooks and functions
In order to abort a `beforeSave` or any hook, you now need to throw an error:
```js
// after with async/await
Parse.Cloud.beforeSave('MyClassName', async (request) => {
// valid, will result in a Parse.Error(SCRIPT_FAILED, 'Something went wrong')
throw 'Something went wrong'
// also valid, will fail with Parse.Error(SCRIPT_FAILED, 'Something else went wrong')
throw new Error('Something else went wrong')
// using a Parse.Error is also valid
throw new Parse.Error(1001, 'My error')
});
```
## Migrating Functions
Cloud Functions can be migrated the same way as cloud code hooks.
In functions, the response object is not passed anymore, as it is in cloud code hooks
Continuing with the image downloader example:
```js
// before
Parse.Cloud.define('downloadImage', function(request, response) {
Parse.Cloud.httpRequest({
url: request.params.url,
success: function(contents) {
const file = new Parse.File(request.params.name, { base64: contents.buffer.toString('base64') });
file.save({
success: function() {
response.success(file);
},
error: response.error
});
},
error: response.error
});
});
```
You would call this method with:
```js
Parse.Cloud.run('downloadImage',{
url: 'https://example.com/file',
name: 'my-file'
});
```
To migrate this function you would follow the same practices as the ones before, and we'll jump right away to the async/await implementation:
```js
// after with async/await
Parse.Cloud.define('downloadImage', async (request) => {
const {
url, name
} = request.params;
const response = await Parse.Cloud.httpRequest({ url });
const file = new Parse.File(name, { base64: response.buffer.toString('base64') });
await file.save();
return file;
});
```
## Migrating jobs
As with hooks and functions, jobs don't have a status object anymore.
The message method has been moved to the request object.
```js
// before
Parse.Cloud.job('downloadImageJob', function(request, status) {
var query = new Parse.Query('ImagesToDownload');
query.find({
success: function(images) {
var done = 0;
for (var i = 0; i<images.length; i++) {
var image = images[i];
status.message('Doing '+image.get('url'));
Parse.Cloud.httpRequest({
url: image.get('url'),
success: function(contents) {
status.message('Got '+image.get('url'));
const file = new Parse.File(image.get('name'), { base64: contents.buffer.toString('base64') });
file.save({
success: function() {
status.message('Saved '+image.get('url'));
done++;
if (done == images.length) {
status.success();
}
},
error: function() {
response.message('Error '+image.get('url'));
done++;
if (done == images.length) {
status.error();
}
}
});
},
error: status.error
});
}
}
});
});
```
```js
Parse.Cloud.job('downloadImageJob', async function(request) {
const query = new Parse.Query('ImagesToDownload');
const images = await query.find();
const promises = images.map(async (image) => {
request.message('Doing ' + image.get('url'));
const contents = await Parse.Cloud.httpRequest({
url: image.get('url')
});
request.message('Got ' + image.get('url'));
const file = new Parse.File(image.get('name'), { base64: contents.buffer.toString('base64') });
await file.save();
request.message('Saved ' + image.get('url'));
});
await Promise.all(promises);
});
```
As you can see the new implementation is more concise, easier to read and maintain.
If you encounter a problem or discover an issue with this guide, or with any Parse Community project, feel free to [reach out on github](https://github.com/parse-community/parse-server/issues/new/choose)
+573 -2
View File
@@ -1,5 +1,576 @@
## Parse Server Changelog
### master
[Full Changelog](https://github.com/parse-community/parse-server/compare/3.4.4...master)
### 3.4.3
[Full Changelog](https://github.com/parse-community/parse-server/compare/3.4.3...3.4.4)
Fix: Commit changes
### 3.4.3
[Full Changelog](https://github.com/parse-community/parse-server/compare/3.4.2...3.4.3)
Fix: Use changes in master to travis configuration to enable pushing to npm and gh_pages. See diff for details.
### 3.4.2
[Full Changelog](https://github.com/parse-community/parse-server/compare/3.4.1...3.4.2)
Fix: In my haste to get a [Security Fix](https://github.com/parse-community/parse-server/security/advisories/GHSA-2479-qvv7-47qq) out, I added [8709daf](https://github.com/parse-community/parse-server/commit/8709daf698ea69b59268cb66f0f7cee75b52daa5) to master instead of to 3.4.1. This commit fixes that. [Arthur Cinader](https://github.com/acinader)
### 3.4.1
[Full Changelog](https://github.com/parse-community/parse-server/compare/3.4.0...3.4.1)
Security Fix: see Advisory: [GHSA-2479-qvv7-47q](https://github.com/parse-community/parse-server/security/advisories/GHSA-2479-qvv7-47qq) for details [8709daf](https://github.com/parse-community/parse-server/commit/8709daf698ea69b59268cb66f0f7cee75b52daa5). Big thanks to: [Benjamin Simonsson](https://github.com/BenniPlejd) for identifying the issue and promptly bringing it to the Parse Community's attention and also big thanks to the indefatigable [Diamond Lewis](https://github.com/dplewis) for crafting a failing test and then a solution within an hour of the report.
### 3.4.0
[Full Changelog](https://github.com/parse-community/parse-server/compare/3.3.0...3.4.0)
- NEW: Aggregate supports group by date fields [#5538](https://github.com/parse-community/parse-server/pull/5538) thanks to [Antonio Davi Macedo Coelho de Castro](https://github.com/davimacedo)
- NEW: API for Read Preferences [#3963](https://github.com/parse-community/parse-server/pull/3963) thanks to [Antonio Davi Macedo Coelho de Castro](https://github.com/davimacedo)
- NEW: Add Redis options for LiveQuery [#5584](https://github.com/parse-community/parse-server/pull/5584) thanks to [Diamond Lewis](https://github.com/dplewis)
- NEW: Add Direct Access option for Server Config [#5550](https://github.com/parse-community/parse-server/pull/5550) thanks to [Diamond Lewis](https://github.com/dplewis)
- FIX: updating mixed array in Postgres [#5552](https://github.com/parse-community/parse-server/pull/5552) thanks to [Diamond Lewis](https://github.com/dplewis)
- FIX: notEqualTo GeoPoint Query in Postgres [#5549](https://github.com/parse-community/parse-server/pull/5549) thanks to [Diamond Lewis](https://github.com/dplewis)
- FIX: put the timestamp back in logs that was lost after Winston upgrade [#5571](https://github.com/parse-community/parse-server/pull/5571), thanks [Steven Rowe](https://github.com/mrowe009) and [Arthur Cinader](https://github.com/acinader)
- FIX: Validates permission before calling beforeSave [#5546](https://github.com/parse-community/parse-server/pull/5546) thanks to [Antonio Davi Macedo Coelho de Castro](https://github.com/davimacedo)
- FIX: Remove userSensitiveFields default value. [#5588](https://github.com/parse-community/parse-server/pull/5588) thanks to [William George](https://github.com/awgeorge)
- FIX: Decode Date JSON value in LiveQuery. [#5540](https://github.com/parse-community/parse-server/pull/5540) thanks to [ananfang](https://github.com/ananfang)
### 3.3.0
[Full Changelog](https://github.com/parse-community/parse-server/compare/3.2.3...3.3.0)
- NEW: beforeLogin trigger with support for auth providers ([#5445](https://github.com/parse-community/parse-server/pull/5445)), thanks to [Omair Vaiyani](https://github.com/omairvaiyani)
- NEW: RFC 7662 compliant OAuth2 auth adapter ([#4910](https://github.com/parse-community/parse-server/pull/4910)), thanks to [Müller Zsolt](https://github.com/zsmuller)
- FIX: cannot change password when maxPasswordHistory is 1 ([#5191](https://github.com/parse-community/parse-server/pull/5191)), thanks to [Tulsi Sapkota](https://github.com/Tolsee)
- FIX (Postgres): count being very slow on large Parse Classes' collections ([#5330](https://github.com/parse-community/parse-server/pull/5330)), thanks to [CoderickLamar](https://github.com/CoderickLamar)
- FIX: using per-key basis queue ([#5420](https://github.com/parse-community/parse-server/pull/5420)), thanks to [Georges Jamous](https://github.com/georgesjamous)
- FIX: issue on count with Geo constraints and mongo ([#5286](https://github.com/parse-community/parse-server/pull/5286)), thanks to [Julien Quéré](https://github.com/jlnquere)
### 3.2.3
[Full Changelog](https://github.com/parse-community/parse-server/compare/3.2.2...3.2.3)
- Correct previous release with patch that is fully merged
### 3.2.2
[Full Changelog](https://github.com/parse-community/parse-server/compare/3.2.1...3.2.2)
- Security fix to properly process userSensitiveFields when parse-server is started with
../lib/cli/parse-server [#5463](https://github.com/parse-community/parse-server/pull/5463
)
### 3.2.1
[Full Changelog](https://github.com/parse-community/parse-server/compare/3.2.0...3.2.1)
- Increment package.json version to match the deployment tag
### 3.2.0
[Full Changelog](https://github.com/parse-community/parse-server/compare/3.1.3...3.2.0)
- NEW: Support accessing sensitive fields with an explicit ACL. Not documented yet, see [tests](https://github.com/parse-community/parse-server/blob/f2c332ea6a984808ad5b2e3ce34864a20724f72b/spec/UserPII.spec.js#L526) for examples
- Upgrade Parse SDK JS to 2.3.1 [#5457](https://github.com/parse-community/parse-server/pull/5457)
- Hides token contents in logStartupOptions if they arrive as a buffer [#6a9380](https://github.com/parse-community/parse-server/commit/6a93806c62205a56a8f4e3b8765848c552510337)
- Support custom message for password requirements [#5399](https://github.com/parse-community/parse-server/pull/5399)
- Support for Ajax password reset [#5332](https://github.com/parse-community/parse-server/pull/5332)
- Postgres: Refuse to build unsafe JSON lists for contains [#5337](https://github.com/parse-community/parse-server/pull/5337)
- Properly handle return values in beforeSave [#5228](https://github.com/parse-community/parse-server/pull/5228)
- Fixes issue when querying user roles [#5276](https://github.com/parse-community/parse-server/pull/5276)
- Fixes issue affecting update with CLP [#5269](https://github.com/parse-community/parse-server/pull/5269)
### 3.1.3
[Full Changelog](https://github.com/parse-community/parse-server/compare/3.1.2...3.1.3)
- Postgres: Fixes support for global configuration
- Postgres: Fixes support for numeric arrays
- Postgres: Fixes issue affecting queries on empty arrays
- LiveQuery: Adds support for transmitting the original object
- Queries: Use estimated count if query is empty
- Docker: Reduces the size of the docker image to 154Mb
### 3.1.2
[Full Changelog](https://github.com/parse-community/parse-server/compare/3.1.1...3.1.2)
- Removes dev script, use TDD instead of server.
- Removes nodemon and problematic dependencies.
- Addressed event-stream security debacle.
### 3.1.1
[Full Changelog](https://github.com/parse-community/parse-server/compare/3.1.0...3.1.1)
#### Improvements:
* Fixes issue that would prevent users with large number of roles to resolve all of them [Antoine Cormouls](https://github.com/Moumouls) (#5131, #5132)
* Fixes distinct query on special fields ([#5144](https://github.com/parse-community/parse-server/pull/5144))
### 3.1.0
[Full Changelog](https://github.com/parse-community/parse-server/compare/3.0.0...3.1.0)
#### Breaking Changes:
* Return success on sendPasswordResetEmail even if email not found. (#7fe4030)
#### Security Fix:
* Expire password reset tokens on email change (#5104)
#### Improvements:
* Live Query CLPs (#4387)
* Reduces number of calls to injectDefaultSchema (#5107)
* Remove runtime dependency on request (#5076)
#### Bug fixes:
* Fixes issue with vkontatke authentication (#4977)
* Use the correct function when validating google auth tokens (#5018)
* fix unexpected 'delete' trigger issue on LiveQuery (#5031)
* Improves performance for roles and ACL's in live query server (#5126)
### 3.0.0
[Full Changelog](https://github.com/parse-community/parse-server/compare/2.8.4...3.0.0)
`parse-server` 3.0.0 comes with brand new handlers for cloud code. It now fully supports promises and async / await.
For more informations, visit the v3.0.0 [migration guide](https://github.com/parse-community/parse-server/blob/master/3.0.0.md).
#### Breaking changes:
* Cloud Code handlers have a new interface based on promises.
* response.success / response.error are removed in Cloud Code
* Cloud Code runs with Parse-SDK 2.0
* The aggregate now require aggregates to be passed in the form: `{"pipeline": [...]}` (REST Only)
#### Improvements:
* Adds Pipeline Operator to Aggregate Router.
* Adds documentations for parse-server's adapters, constructors and more.
* Adds ability to pass a context object between `beforeSave` and `afterSave` affecting the same object.
#### Bug Fixes:
* Fixes issue that would crash the server when mongo objects had undefined values [#4966](https://github.com/parse-community/parse-server/issues/4966)
* Fixes issue that prevented ACL's from being used with `select` (see [#571](https://github.com/parse-community/Parse-SDK-JS/issues/571))
#### Dependency updates:
* [@parse/simple-mailgun-adapter@1.1.0](https://www.npmjs.com/package/@parse/simple-mailgun-adapter)
* [mongodb@3.1.3](https://www.npmjs.com/package/mongodb)
* [request@2.88.0](https://www.npmjs.com/package/request)
##### Devevelopment Dependencies Updates:
* [@parse/minami@1.0.0](https://www.npmjs.com/package/@parse/minami)
* [deep-diff@1.0.2](https://www.npmjs.com/package/deep-diff)
* [flow-bin@0.79.0](https://www.npmjs.com/package/flow-bin)
* [jsdoc@3.5.5](https://www.npmjs.com/package/jsdoc)
* [jsdoc-babel@0.4.0](https://www.npmjs.com/package/jsdoc-babel)
### 2.8.4
[Full Changelog](https://github.com/parse-community/parse-server/compare/2.8.3...2.8.4)
#### Improvements:
* Adds ability to forward errors to express handler (#4697)
* Adds ability to increment the push badge with an arbitrary value (#4889)
* Adds ability to preserve the file names when uploading (#4915)
* `_User` now follow regular ACL policy. Letting administrator lock user out. (#4860) and (#4898)
* Ensure dates are properly handled in aggregates (#4743)
* Aggregates: Improved support for stages sharing the same name
* Add includeAll option
* Added verify password to users router and tests. (#4747)
* Ensure read preference is never overriden, so DB config prevails (#4833)
* add support for geoWithin.centerSphere queries via withJSON (#4825)
* Allow sorting an object field (#4806)
* Postgres: Don't merge JSON fields after save() to keep same behaviour as MongoDB (#4808) (#4815)
#### Dependency updates
* [commander@2.16.0](https://www.npmjs.com/package/commander)
* [mongodb@3.1.1](https://www.npmjs.com/package/mongodb)
* [pg-promise@8.4.5](https://www.npmjs.com/package/pg-promise)
* [ws@6.0.0](https://www.npmjs.com/package/ws)
* [bcrypt@3.0.0](https://www.npmjs.com/package/bcrypt)
* [uws@10.148.1](https://www.npmjs.com/package/uws)
##### Devevelopment Dependencies Updates:
* [cross-env@5.2.0](https://www.npmjs.com/package/cross-env)
* [eslint@5.0.0](https://www.npmjs.com/package/eslint)
* [flow-bin@0.76.0](https://www.npmjs.com/package/flow-bin)
* [mongodb-runner@4.0.0](https://www.npmjs.com/package/mongodb-runner)
* [nodemon@1.18.1](https://www.npmjs.com/package/nodemon)
* [nyc@12.0.2](https://www.npmjs.com/package/nyc)
* [request-promise@4.2.2](https://www.npmjs.com/package/request-promise)
* [supports-color@5.4.0](https://www.npmjs.com/package/supports-color)
### 2.8.3
[Full Changelog](https://github.com/parse-community/parse-server/compare/2.8.2...2.8.3)
#### Improvements:
* Adds support for JS SDK 2.0 job status header
* Removes npm-git scripts as npm supports using git repositories that build, thanks to [Florent Vilmart](https://github.com/flovilmart)
### 2.8.2
[Full Changelog](https://github.com/parse-community/parse-server/compare/2.8.1...2.8.2)
##### Bug Fixes:
* Ensure legacy users without ACL's are not locked out, thanks to [Florent Vilmart](https://github.com/flovilmart)
#### Improvements:
* Use common HTTP agent to increase webhooks performance, thanks to [Tyler Brock](https://github.com/TylerBrock)
* Adds withinPolygon support for Polygon objects, thanks to [Mads Bjerre](https://github.com/madsb)
#### Dependency Updates:
* [ws@5.2.0](https://www.npmjs.com/package/ws)
* [commander@2.15.1](https://www.npmjs.com/package/commander)
* [nodemon@1.17.5](https://www.npmjs.com/package/nodemon)
##### Devevelopment Dependencies Updates:
* [flow-bin@0.73.0](https://www.npmjs.com/package/flow-bin)
* [cross-env@5.1.6](https://www.npmjs.com/package/cross-env)
* [gaze@1.1.3](https://www.npmjs.com/package/gaze)
* [deepcopy@1.0.0](https://www.npmjs.com/package/deepcopy)
* [deep-diff@1.0.1](https://www.npmjs.com/package/deep-diff)
### 2.8.1
[Full Changelog](https://github.com/parse-community/parse-server/compare/2.8.1...2.8.0)
Ensure all the files are properly exported to the final package.
### 2.8.0
[Full Changelog](https://github.com/parse-community/parse-server/compare/2.8.0...2.7.4)
#### New Features
* Adding Mongodb element to add `arrayMatches` the #4762 (#4766), thanks to [Jérémy Piednoel](https://github.com/jeremypiednoel)
* Adds ability to Lockout users (#4749), thanks to [Florent Vilmart](https://github.com/flovilmart)
#### Bug fixes:
* Fixes issue when using afterFind with relations (#4752), thanks to [Florent Vilmart](https://github.com/flovilmart)
* New query condition support to match all strings that starts with some other given strings (#3864), thanks to [Eduard Bosch Bertran](https://github.com/eduardbosch)
* Allow creation of indices on default fields (#4738), thanks to [Claire Neveu](https://github.com/ClaireNeveu)
* Purging empty class (#4676), thanks to [Diamond Lewis](https://github.com/dplewis)
* Postgres: Fixes issues comparing to zero or false (#4667), thanks to [Diamond Lewis](https://github.com/dplewis)
* Fix Aggregate Match Pointer (#4643), thanks to [Diamond Lewis](https://github.com/dplewis)
#### Improvements:
* Allow Parse.Error when returning from Cloud Code (#4695), thanks to [Saulo Tauil](https://github.com/saulogt)
* Fix typo: "requrest" -> "request" (#4761), thanks to [Joseph Frazier](https://github.com/josephfrazier)
* Send version for Vkontakte API (#4725), thanks to [oleg](https://github.com/alekoleg)
* Ensure we respond with invalid password even if email is unverified (#4708), thanks to [dblythy](https://github.com/dblythy)
* Add _password_history to default sensitive data (#4699), thanks to [Jong Eun Lee](https://github.com/yomybaby)
* Check for node version in postinstall script (#4657), thanks to [Diamond Lewis](https://github.com/dplewis)
* Remove FB Graph API version from URL to use the oldest non deprecated version, thanks to [SebC](https://github.com/SebC99)
#### Dependency Updates:
* [@parse/push-adapter@2.0.3](https://www.npmjs.com/package/@parse/push-adapter)
* [@parse/simple-mailgun-adapter@1.0.2](https://www.npmjs.com/package/@parse/simple-mailgun-adapter)
* [uws@10.148.0](https://www.npmjs.com/package/uws)
* [body-parser@1.18.3](https://www.npmjs.com/package/body-parser)
* [mime@2.3.1](https://www.npmjs.com/package/mime)
* [request@2.85.0](https://www.npmjs.com/package/request)
* [mongodb@3.0.7](https://www.npmjs.com/package/mongodb)
* [bcrypt@2.0.1](https://www.npmjs.com/package/bcrypt)
* [ws@5.1.1](https://www.npmjs.com/package/ws)
##### Devevelopment Dependencies Updates:
* [cross-env@5.1.5](https://www.npmjs.com/package/cross-env)
* [flow-bin@0.71.0](https://www.npmjs.com/package/flow-bin)
* [deep-diff@1.0.0](https://www.npmjs.com/package/deep-diff)
* [nodemon@1.17.3](https://www.npmjs.com/package/nodemon)
### 2.7.4
[Full Changelog](https://github.com/parse-community/parse-server/compare/2.7.4...2.7.3)
#### Bug Fixes:
* Fixes an issue affecting polygon queries, thanks to [Diamond Lewis](https://github.com/dplewis)
#### Dependency Updates:
* [pg-promise@8.2.1](https://www.npmjs.com/package/pg-promise)
##### Development Dependencies Updates:
* [nodemon@1.17.1](https://www.npmjs.com/package/nodemon)
### 2.7.3
[Full Changelog](https://github.com/parse-community/parse-server/compare/2.7.3...2.7.2)
#### Improvements:
* Improve documentation for LiveQuery options, thanks to [Arthur Cinader](https://github.com/acinader)
* Improve documentation for using cloud code with docker, thanks to [Stephen Tuso](https://github.com/stephentuso)
* Adds support for Facebook's AccountKit, thanks to [6thfdwp](https://github.com/6thfdwp)
* Disable afterFind routines when running aggregates, thanks to [Diamond Lewis](https://github.com/dplewis)
* Improve support for distinct aggregations of nulls, thanks to [Diamond Lewis](https://github.com/dplewis)
* Regenreate the email verification token when requesting a new email, thanks to [Benjamin Wilson Friedman](https://github.com/montymxb)
#### Bug Fixes:
* Fix issue affecting readOnly masterKey and purge command, thanks to [AreyouHappy](https://github.com/AreyouHappy)
* Fixes Issue unsetting in beforeSave doesn't allow object creation, thanks to [Diamond Lewis](https://github.com/dplewis)
* Fixes issue crashing server on invalid live query payload, thanks to [fridays](https://github.com/fridays)
* Fixes issue affecting postgres storage adapter "undefined property '__op'", thanks to [Tyson Andre](https://github,com/TysonAndre)
#### Dependency Updates:
* [winston@2.4.1](https://www.npmjs.com/package/winston)
* [pg-promise@8.2.0](https://www.npmjs.com/package/pg-promise)
* [commander@2.15.0](https://www.npmjs.com/package/commander)
* [lru-cache@4.1.2](https://www.npmjs.com/package/lru-cache)
* [parse@1.11.1](https://www.npmjs.com/package/parse)
* [ws@5.0.0](https://www.npmjs.com/package/ws)
* [mongodb@3.0.4](https://www.npmjs.com/package/mongodb)
* [lodash@4.17.5](https://www.npmjs.com/package/lodash)
##### Devevelopment Dependencies Updates:
* [cross-env@5.1.4](https://www.npmjs.com/package/cross-env)
* [flow-bin@0.67.1](https://www.npmjs.com/package/flow-bin)
* [jasmine@3.1.0](https://www.npmjs.com/package/jasmine)
* [parse@1.11.1](https://www.npmjs.com/package/parse)
* [babel-eslint@8.2.2](https://www.npmjs.com/package/babel-eslint)
* [nodemon@1.15.0](https://www.npmjs.com/package/nodemon)
### 2.7.2
[Full Changelog](https://github.com/parse-community/parse-server/compare/2.7.2...2.7.1)
#### Improvements:
* Improved match aggregate
* Do not mark the empty push as failed
* Support pointer in aggregate query
* Introduces flow types for storage
* Postgres: Refactoring of Postgres Storage Adapter
* Postgres: Support for multiple projection in aggregate
* Postgres: performance optimizations
* Adds infos about vulnerability disclosures
* Adds ability to login with email when provided as username
#### Bug Fixes
* Scrub Passwords with URL Encoded Characters
* Fixes issue affecting using sorting in beforeFind
#### Dependency Updates:
* [commander@2.13.0](https://www.npmjs.com/package/commander)
* [semver@5.5.0](https://www.npmjs.com/package/semver)
* [pg-promise@7.4.0](https://www.npmjs.com/package/pg-promise)
* [ws@4.0.0](https://www.npmjs.com/package/ws)
* [mime@2.2.0](https://www.npmjs.com/package/mime)
* [parse@1.11.0](https://www.npmjs.com/package/parse)
##### Devevelopment Dependencies Updates:
* [nodemon@1.14.11](https://www.npmjs.com/package/nodemon)
* [flow-bin@0.64.0](https://www.npmjs.com/package/flow-bin)
* [jasmine@2.9.0](https://www.npmjs.com/package/jasmine)
* [cross-env@5.1.3](https://www.npmjs.com/package/cross-env)
### 2.7.1
[Full Changelog](https://github.com/parse-community/parse-server/compare/2.7.1...2.7.0)
:warning: Fixes a security issue affecting Class Level Permissions
* Adds support for dot notation when using matchesKeyInQuery, thanks to [Henrik](https://github.com/bohemima) and [Arthur Cinader](https://github.com/acinader)
### 2.7.0
[Full Changelog](https://github.com/parse-community/parse-server/compare/2.7.0...2.6.5)
:warning: This version contains an issue affecting Class Level Permissions on mongoDB. Please upgrade to 2.7.1.
Starting parse-server 2.7.0, the minimun nodejs version is 6.11.4, please update your engines before updating parse-server
#### New Features:
* Aggregation endpoints, thanks to [Diamond Lewis](https://github.com/dplewis)
* Adds indexation options onto Schema endpoints, thanks to [Diamond Lewis](https://github.com/dplewis)
#### Bug fixes:
* Fixes sessionTokens being overridden in 'find' (#4332), thanks to [Benjamin Wilson Friedman](https://github.com/montymxb)
* Proper `handleShutdown()` feature to close database connections (#4361), thanks to [CHANG, TZU-YEN](https://github.com/trylovetom)
* Fixes issue affecting state of _PushStatus objects, thanks to [Benjamin Wilson Friedman](https://github.com/montymxb)
* Fixes issue affecting calling password reset password pages with wrong appid, thanks to [Bryan de Leon](https://github.com/bryandel)
* Fixes issue affecting duplicates _Sessions on successive logins, thanks to [Florent Vilmart](https://github.com/flovilmart)
#### Improvements:
* Updates contributing guides, and improves windows support, thanks to [Addison Elliott](https://github.com/addisonelliott)
* Uses new official scoped packaged, thanks to [Florent Vilmart](https://github.com/flovilmart)
* Improves health checks responses, thanks to [Benjamin Wilson Friedman](https://github.com/montymxb)
* Add password confirmation to choose_password, thanks to [Worathiti Manosroi](https://github.com/pungme)
* Improve performance of relation queries, thanks to [Florent Vilmart](https://github.com/flovilmart)
#### Dependency Updates:
* [commander@2.12.1](https://www.npmjs.com/package/commander)
* [ws@3.3.2](https://www.npmjs.com/package/ws)
* [uws@9.14.0](https://www.npmjs.com/package/uws)
* [pg-promise@7.3.2](https://www.npmjs.com/package/pg-promise)
* [parse@1.10.2](https://www.npmjs.com/package/parse)
* [pg-promise@7.3.1](https://www.npmjs.com/package/pg-promise)
##### Devevelopment Dependencies Updates:
* [cross-env@5.1.1](https://www.npmjs.com/package/cross-env)
### 2.6.5
[Full Changelog](https://github.com/ParsePlatform/parse-server/compare/2.6.5...2.6.4)
#### New Features:
* Adds support for read-only masterKey, thanks to [Florent Vilmart](https://github.com/flovilmart)
* Adds support for relative time queries (mongodb only), thanks to [Marvel Mathew](https://github.com/marvelm)
#### Improvements:
* Handle possible afterSave exception, thanks to [Benjamin Wilson Friedman](https://github.com/montymxb)
* Add support for expiration interval in Push, thanks to [Marvel Mathew](https://github.com/marvelm)
#### Bug Fixes:
* The REST API key was improperly inferred from environment when using the CLI, thanks to [Florent Vilmart](https://github.com/flovilmart)
### 2.6.4
[Full Changelog](https://github.com/ParsePlatform/parse-server/compare/2.6.4...2.6.3)
#### Improvements:
* Improves management of configurations and default values, thanks to [Florent Vilmart](https://github.com/flovilmart)
* Adds ability to start ParseServer with `ParseServer.start(options)`, thanks to [Florent Vilmart](https://github.com/flovilmart)
* Adds request original IP to cloud code hooks, thanks to [Gustav Ahlberg](https://github.com/Gyran)
* Corrects some outdated links, thanks to [Benjamin Wilson Friedman](https://github.com/montymxb)
* Adds serverURL validation on startup, thanks to [Benjamin Wilson Friedman](https://github.com/montymxb)
* Adds ability to login with POST requests alongside GET, thanks to [Benjamin Wilson Friedman](https://github.com/montymxb)
* Adds ability to login with email, instead of username, thanks to [Florent Vilmart](https://github.com/flovilmart)
#### Bug Fixes:
* Fixes issue affecting beforeSaves and increments, thanks to [Benjamin Wilson Friedman](https://github.com/montymxb)
#### Dependency Updates:
* [parse-server-push-adapter@2.0.2](https://www.npmjs.com/package/parse-server-push-adapter)
* [semver@5.4.1](https://www.npmjs.com/package/semver)
* [pg-promise@7.0.3](https://www.npmjs.com/package/pg-promise)
* [mongodb@2.2.33](https://www.npmjs.com/package/mongodb)
* [parse@1.10.1](https://www.npmjs.com/package/parse)
* [express@4.16.0](https://www.npmjs.com/package/express)
* [mime@1.4.1](https://www.npmjs.com/package/mime)
* [parse-server-simple-mailgun-adapter@1.0.1](https://www.npmjs.com/package/parse-server-simple-mailgun-adapter)
##### Devevelopment Dependencies Updates:
* [babel-preset-env@1.6.1](https://www.npmjs.com/package/babel-preset-env)
* [cross-env@5.1.0](https://www.npmjs.com/package/cross-env)
* [mongodb-runner@3.6.1](https://www.npmjs.com/package/mongodb-runner)
* [eslint-plugin-flowtype@2.39.1](https://www.npmjs.com/package/eslint-plugin-flowtype)
* [eslint@4.9.0](https://www.npmjs.com/package/eslint)
### 2.6.3
[Full Changelog](https://github.com/ParsePlatform/parse-server/compare/2.6.2...2.6.3)
#### Improvements:
* Queries on Pointer fields with `$in` and `$nin` now supports list of objectId's, thanks to [Florent Vilmart](https://github.com/flovilmart)
* LiveQueries on `$in` and `$nin` for pointer fields work as expected thanks to [Florent Vilmart](https://github.com/flovilmart)
* Also remove device token when APNS error is BadDeviceToken, thanks to [Mauricio Tollin](https://github.com/)
* LRU cache is not available on the ParseServer object, thanks to [Tyler Brock](https://github.com/tbrock)
* Error messages are more expressive, thanks to [Tyler Brock](https://github.com/tbrock)
* Postgres: Properly handle undefined field values, thanks to [Diamond Lewis](https://github.com/dlewis)
* Updating with two GeoPoints fails correctly, thanks to [Anthony Mosca](https://github.com/aontas)
#### New Features:
* Adds ability to set a maxLimit on server configuration for queries, thanks to [Chris Norris](https://github.com/)
#### Bug fixes:
* Fixes issue affecting reporting `_PushStatus` with misconfigured serverURL, thanks to [Florent Vilmart](https://github.com/flovilmart)
* Fixes issue affecting deletion of class that doesn't exist, thanks to [Diamond Lewis](https://github.com/dlewis)
#### Dependency Updates:
* [winston@2.4.0](https://www.npmjs.com/package/winston)
* [pg-promise@6.10.2](https://www.npmjs.com/package/pg-promise)
* [winston-daily-rotate-file@1.6.0](https://www.npmjs.com/package/winston-daily-rotate-file)
* [request@2.83.0](https://www.npmjs.com/package/request)
* [body-parser@1.18.2](https://www.npmjs.com/package/body-parser)
##### Devevelopment Dependencies Updates:
* [request-promise@4.2.2](https://www.npmjs.com/package/request-promise)
* [eslint@4.7.1](https://www.npmjs.com/package/eslint)
### 2.6.2
[Full Changelog](https://github.com/ParsePlatform/parse-server/compare/2.6.1...2.6.2)
#### Improvements:
* PushWorker/PushQueue channels are properly prefixed with the Parse applicationId, thanks to [Marvel Mathew](https://github.com/marvelm)
* You can use Parse.Cloud.afterSave hooks on _PushStatus
* You can use Parse.Cloud.onLiveQueryEvent to track the number of clients and subscriptions
* Adds support for more fields from the Audience class.
#### New Features:
* Push: Adds ability to track sentPerUTC offset if your push scheduler supports it.
* Push: Adds support for cleaning up invalid deviceTokens from _Installation (PARSE_SERVER_CLEANUP_INVALID_INSTALLATIONS=1).
#### Dependency Updates:
* [ws@3.2.0](https://www.npmjs.com/package/ws)
* [pg-promise@6.5.3](https://www.npmjs.com/package/pg-promise)
* [winston-daily-rotate-file@1.5.0](https://www.npmjs.com/package/winston-daily-rotate-file)
* [body-parser@1.18.1](https://www.npmjs.com/package/body-parser)
##### Devevelopment Dependencies Updates:
* [nodemon@1.12.1](https://www.npmjs.com/package/nodemon)
* [mongodb-runner@3.6.0](https://www.npmjs.com/package/mongodb-runner)
* [babel-eslint@8.0.0](https://www.npmjs.com/package/babel-eslint)
### 2.6.1
[Full Changelog](https://github.com/ParsePlatform/parse-server/compare/2.6.0...2.6.1)
#### Improvements:
* Improves overall performance of the server, more particularly with large query results.
* Improves performance of InMemoryCacheAdapter by removing serialization.
* Improves logging performance by skipping necessary log calls.
* Refactors object routers to simplify logic.
* Adds automatic indexing on $text indexes, thanks to [Diamon Lewis](https://github.com/dplewis)
#### New Features:
* Push: Adds ability to send localized pushes according to the _Installation localeIdentifier
* Push: proper support for scheduling push in user's locale time, thanks to [Marvel Mathew](https://github.com/marvelm)
* LiveQuery: Adds ability to use LiveQuery with a masterKey, thanks to [Jeremy May](https://github.com/kenishi)
#### Bug Fixes:
* Fixes an issue that would duplicate Session objects per userId-installationId pair.
* Fixes an issue affecting pointer permissions introduced in this release.
* Fixes an issue that would prevent displaying audiences correctly in dashboard.
* Fixes an issue affecting preventLoginWithUnverifiedEmail upon signups.
#### Dependency Updates:
* [pg-promise@6.3.2](https://www.npmjs.com/package/pg-promise)
* [body-parser@1.18.0](https://www.npmjs.com/package/body-parser)
* [nodemon@1.11.1](https://www.npmjs.com/package/nodemon)
##### Devevelopment Dependencies Updates:
* [babel-cli@6.26.0](https://www.npmjs.com/package/babel-cli)
### 2.6.0
[Full Changelog](https://github.com/ParsePlatform/parse-server/compare/2.5.3...2.6.0)
#### Breaking Changes:
* [parse-server-s3-adapter@1.2.0](https://www.npmjs.com/package/parse-server-s3-adapter): A new deprecation notice is introduced with parse-server-s3-adapter's version 1.2.0. An upcoming release will remove passing key and password arguments. AWS credentials should be set using AWS best practices. See the [Deprecation Notice for AWS credentials]( https://github.com/parse-server-modules/parse-server-s3-adapter/blob/master/README.md#deprecation-notice----aws-credentials) section of the adapter's README.
#### New Features
* Polygon is fully supported as a type, thanks to [Diamond Lewis](https://github.com/dplewis)
* Query supports PolygonContains, thanks to [Diamond Lewis](https://github.com/dplewis)
#### Improvements
* Postgres: Adds support nested contains and containedIn, thanks to [Diamond Lewis](https://github.com/dplewis)
* Postgres: Adds support for `null` in containsAll queries, thanks to [Diamond Lewis](https://github.com/dplewis)
* Cloud Code: Request headers are passed to the cloud functions, thanks to [miguel-s](https://github.com/miguel-s)
* Push: All push queries now filter only where deviceToken exists
#### Bug Fixes:
* Fixes issue affecting updates of _User objects when authData was passed.
* Push: Pushing to an empty audience should now properly report a failed _PushStatus
* Linking Users: Fixes issue affecting linking users with sessionToken only
#### Dependency Updates:
* [ws@3.1.0](https://www.npmjs.com/package/ws)
* [mime@1.4.0](https://www.npmjs.com/package/mime)
* [semver@5.4.0](https://www.npmjs.com/package/semver)
* [uws@8.14.1](https://www.npmjs.com/package/uws)
* [bcrypt@1.0.3](https://www.npmjs.com/package/bcrypt)
* [mongodb@2.2.31](https://www.npmjs.com/package/mongodb)
* [redis@2.8.0](https://www.npmjs.com/package/redis)
* [pg-promise@6.3.1](https://www.npmjs.com/package/pg-promise)
* [commander@2.11.0](https://www.npmjs.com/package/commander)
##### Devevelopment Dependencies Updates:
* [jasmine@2.8.0](https://www.npmjs.com/package/jasmine)
* [babel-register@6.26.0](https://www.npmjs.com/package/babel-register)
* [babel-core@6.26.0](https://www.npmjs.com/package/babel-core)
* [cross-env@5.0.2](https://www.npmjs.com/package/cross-env)
### 2.5.3
[Full Changelog](https://github.com/ParsePlatform/parse-server/compare/2.5.2...2.5.3)
#### New Features:
* badge property on android installations will now be set as on iOS (#3970), thanks to [Florent Vilmart](https://github.com/flovilmart)
#### Bug Fixes:
* Fixes incorrect number parser for cache options
### 2.5.2
[Full Changelog](https://github.com/ParsePlatform/parse-server/compare/2.5.1...2.5.2)
#### Improvements:
* Restores ability to run on node >= 4.6
* Adds ability to configure cache from CLI
* Removes runtime check for node >= 4.6
### 2.5.1
[Full Changelog](https://github.com/ParsePlatform/parse-server/compare/2.5.0...2.5.1)
@@ -112,7 +683,7 @@ If you experience issues with older versions, please [open a issue](https://gith
* Fix issue affecting overloaded query constraints ([#3723](https://github.com/parse-community/parse-server/pull/3723), [#3678](https://github.com/parse-community/parse-server/pull/3678)), thanks to [Florent Vilmart](https://github.com/flovilmart)
* Properly catch unhandled rejections in _Installation updates ([#3795](https://github.com/parse-community/parse-server/pull/3795)), thanks to [kahoona77](https://github.com/kahoona77)
#### Dependency Updates:
#### Dependency Updates:
* [uws@0.14.5](https://www.npmjs.com/package/uws)
* [mime@1.3.6](https://www.npmjs.com/package/mime)
@@ -768,7 +1339,7 @@ Other fixes by [Mathias Rangel Wulff](https://github.com/mathiasrw)
* Fix: Improve compatability of cloud code beforeSave hook for newly created object
* Fix: ACL creation for master key only objects
* Fix: Allow uploading files without Content-Type
* Fix: Add features to http requrest to match Parse.com
* Fix: Add features to http request to match Parse.com
* Fix: Bugs in development script when running from locations other than project root
* Fix: Can pass query constraints in URL
* Fix: Objects with legacy "_tombstone" key now don't cause issues.
+1 -1
View File
@@ -34,7 +34,7 @@ This Code of Conduct applies both within project spaces and in public spaces whe
## Enforcement
Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at florent@flovilmart.com. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately.
Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at codeofconduct@parseplatform.org. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately.
Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership.
+69 -11
View File
@@ -1,28 +1,86 @@
### Contributing to Parse Server
# Contributing to Parse Server
#### Pull Requests Welcome!
We really want Parse to be yours, to see it grow and thrive in the open source community.
We really want Parse to be yours, to see it grow and thrive in the open source community.
If you are not familiar with Pull Requests and want to know more about them, you can visit the [Creating a pull request](https://help.github.com/articles/creating-a-pull-request/) article. It contains detailed informations about the process.
##### Please Do's
## Setting up the project for debugging and contributing:
* Take testing seriously! Aim to increase the test coverage with every pull request.
* Run the tests for the file you are working on with `npm test spec/MyFile.spec.js`
* Run the tests for the whole project and look at the coverage report to make sure your tests are exhaustive by running `npm test` and looking at (project-root)/lcov-report/parse-server/FileUnderTest.js.html
* Lint your code by running `npm run lint` to make sure all your code is not gonna be rejected by the CI.
* Never publish the lib folder.
### Recommended setup:
##### Run your tests against Postgres (optional)
* [vscode](https://code.visualstudio.com), the popular IDE.
* [Jasmine Test Explorer](https://marketplace.visualstudio.com/items?itemName=hbenl.vscode-jasmine-test-adapter), a very practical test exploration plugin which let you run, debug and see the test results inline.
### Setting up you local machine:
* [Fork](https://github.com/parse-community/parse-server) this project and clone the fork on your local machine:
```sh
$ git clone https://github.com/parse-community/parse-server
$ cd parse-server # go into the clone directory
$ npm install # install all the node dependencies
$ code . # launch vscode
$ npm run watch # run babel watching for local file changes
```
> To launch VS Code from the terminal with the `code` command you first need to follow the [launching from the command line section](https://code.visualstudio.com/docs/setup/mac#_launching-from-the-command-line) in the VS Code setup documentation.
Once you have babel running in watch mode, you can start making changes to parse-server.
### Good to know:
* The lib/ folder is not commited, so never make changes in there.
* Always make changes to files in the `src/` folder.
* All the tests should point to sources in the `lib/` folder.
### Troubleshooting:
*Question*: I modify the code in the src folder but it doesn't seem to have any effect.<br/>
*Answer*: Check that `npm run watch` is running
*Question*: How do I use breakpoints and debug step by step?<br/>
*Answer*: The easiest way is to install [Jasmine Test Explorer](https://marketplace.visualstudio.com/items?itemName=hbenl.vscode-test-explorer), it will let you run selectively tests and debug them.
*Question*: How do I deploy my forked version on my servers?<br/>
*Answer*: In your `package.json`, update the `parse-server` dependency to `https://github.com/MY_USERNAME/parse-server#MY_FEATURE`. Run `npm install`, commit the changes and deploy to your servers.
### Please Do's
* Begin by reading the [Development Guide](http://docs.parseplatform.org/parse-server/guide/#development-guide) to learn how to get started running the parse-server.
* Take testing seriously! Aim to increase the test coverage with every pull request. To obtain the test coverage of the project, run:
* **Windows**: `npm run coverage:win`
* **Unix**: `npm run coverage`
* Run the tests for the file you are working on with the following command:
* **Windows**: `npm run test:win spec/MyFile.spec.js`
* **Unix**: `npm test spec/MyFile.spec.js`
* Run the tests for the whole project to make sure the code passes all tests. This can be done by running the test command for a single file but removing the test file argument. The results can be seen at *<PROJECT_ROOT>/coverage/lcov-report/index.html*.
* Lint your code by running `npm run lint` to make sure the code is not going to be rejected by the CI.
* **Do not** publish the *lib* folder.
### Run your tests against Postgres (optional)
If your pull request introduces a change that may affect the storage or retrieval of objects, you may want to make sure it plays nice with Postgres.
* Run the tests against the postgres database with `PARSE_SERVER_TEST_DB=postgres npm test`. You'll need to have postgres running on your machine and setup [appropriately](https://github.com/parse-community/parse-server/blob/master/.travis.yml#L37)
* The Postgres adapter has a special debugger that traces all the sql commands. You can enable it with setting the environment variable `PARSE_SERVER_LOG_LEVEL=debug`
* If your feature is intended to only work with MongoDB, you should disable PostgreSQL-specific tests with:
- `describe_only_db('mongo')` // will create a `describe` that runs only on mongoDB
- `it_only_db('mongo')` // will make a test that only runs on mongo
- `it_exclude_dbs(['postgres'])` // will make a test that runs against all DB's but postgres
##### Code of Conduct
### Generate Parse Server Config Definition
If you want to make changes to [Parse Server Configuration][config] add the desired configuration to [src/Options/index.js][config-index] and run `npm run definitions`. This will output [src/Options/Definitions.js][config-def] and [src/Options/docs.js][config-docs].
To view docs run `npm run docs` and check the `/out` directory.
### Code of Conduct
This project adheres to the [Contributor Covenant Code of Conduct](https://github.com/parse-community/parse-server/blob/master/CODE_OF_CONDUCT.md). By participating, you are expected to honor this code.
[config]: http://parseplatform.org/parse-server/api/master/ParseServerOptions.html
[config-def]: https://github.com/parse-community/parse-server/blob/master/src/Options/Definitions.js
[config-docs]: https://github.com/parse-community/parse-server/blob/master/src/Options/docs.js
[config-index]: https://github.com/parse-community/parse-server/blob/master/src/Options/index.js
+22 -12
View File
@@ -1,21 +1,31 @@
FROM node:boron
# Build stage
FROM node:lts-alpine as build
RUN mkdir -p /parse-server
COPY ./ /parse-server/
RUN apk update; \
apk add git;
WORKDIR /tmp
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build
RUN mkdir -p /parse-server/config
VOLUME /parse-server/config
RUN mkdir -p /parse-server/cloud
VOLUME /parse-server/cloud
# Release stage
FROM node:lts-alpine as release
VOLUME /parse-server/cloud /parse-server/config
WORKDIR /parse-server
RUN npm install && \
npm run build
COPY package*.json ./
RUN npm ci --production --ignore-scripts
COPY bin bin
COPY public_html public_html
COPY views views
COPY --from=build /tmp/lib lib
RUN mkdir -p logs && chown -R node: logs
ENV PORT=1337
USER node
EXPOSE $PORT
ENTRYPOINT ["npm", "start", "--"]
ENTRYPOINT ["node", "./bin/parse-server"]
+182 -82
View File
@@ -1,22 +1,51 @@
![Parse Server logo](.github/parse-server-logo.png?raw=true)
![Parse Server logo](https://github.com/parse-community/parse-server/raw/master/.github/parse-server-logo.png)
[![Backers on Open Collective](https://opencollective.com/parse-server/backers/badge.svg)](#backers) [![Sponsors on Open Collective](https://opencollective.com/parse-server/sponsors/badge.svg)](#sponsors)
[![Build Status](https://img.shields.io/travis/parse-community/parse-server/master.svg?style=flat)](https://travis-ci.org/parse-community/parse-server)
[![Coverage Status](https://img.shields.io/codecov/c/github/parse-community/parse-server/master.svg)](https://codecov.io/github/parse-community/parse-server?branch=master)
[![npm version](https://img.shields.io/npm/v/parse-server.svg?style=flat)](https://www.npmjs.com/package/parse-server)
[![Join The Conversation](https://img.shields.io/discourse/https/community.parseplatform.org/topics.svg)](https://community.parseplatform.org/c/parse-server)
[![Greenkeeper badge](https://badges.greenkeeper.io/parse-community/parse-server.svg)](https://greenkeeper.io/)
[![Twitter Follow](https://img.shields.io/twitter/follow/ParsePlatform.svg?label=Follow%20us%20on%20Twitter&style=social)](https://twitter.com/intent/follow?screen_name=ParsePlatform)
[![Join Chat](https://img.shields.io/badge/gitter-join%20chat%20%E2%86%92-brightgreen.svg)](https://gitter.im/ParsePlatform/Chat)
[![](https://img.shields.io/badge/mongodb-3.2-green.svg?logo=mongodb&style=flat)]()
[![](https://img.shields.io/badge/mongodb-3.4-green.svg?logo=mongodb&style=flat)]()
[![](https://img.shields.io/badge/mongodb-3.6-green.svg?logo=mongodb&style=flat)]()
[![](https://img.shields.io/badge/mongodb-4.0-green.svg?logo=mongodb&style=flat)]()
Parse Server is an [open source version of the Parse backend](http://blog.parse.com/announcements/introducing-parse-server-and-the-database-migration-tool/) that can be deployed to any infrastructure that can run Node.js.
Parse Server is an [open source version of the Parse backend](http://blog.parseplatform.org/announcements/introducing-parse-server-and-the-database-migration-tool/) that can be deployed to any infrastructure that can run Node.js.
Parse Server works with the Express web application framework. It can be added to existing web applications, or run by itself.
The full documentation for Parse Server is available in the [wiki](https://github.com/parse-community/parse-server/wiki). The [Parse Server guide](http://docs.parseplatform.org/parse-server/guide/) is a good place to get started. An [API reference](http://parseplatform.org/parse-server/api/) is also available. If you're interested in developing for Parse Server, the [Development guide](http://docs.parseplatform.org/parse-server/guide/#development-guide) will help you get set up.
- [Getting Started](#getting-started)
- [Running Parse Server](#running-parse-server)
- [Locally](#locally)
- [Docker](#inside-a-docker-container)
- [Saving an Object](#saving-your-first-object)
- [Connect an SDK](#connect-your-app-to-parse-server)
- [Running elsewhere](#running-parse-server-elsewhere)
- [Sample Application](#parse-server-sample-application)
- [Parse Server + Express](#parse-server--express)
- [Configuration](#configuration)
- [Basic Options](#basic-options)
- [Client Key Options](#client-key-options)
- [Email Verification & Password Reset](#email-verification-and-password-reset)
- [Using Environment Variables](#using-environment-variables-to-configure-parse-server)
- [Available Adapters](#available-adapters)
- [Configuring File Adapters](#configuring-file-adapters)
- [Logging](#logging)
- [Live Queries](#live-queries)
- [Upgrading to 3.0.0](#upgrading-to-300)
- [Support](#support)
- [Ride the Bleeding Edge](#want-to-ride-the-bleeding-edge)
- [Contributing](#contributing)
- [Backers](#backers)
- [Sponsors](#sponsors)
# Getting Started
[![Greenkeeper badge](https://badges.greenkeeper.io/parse-community/parse-server.svg)](https://greenkeeper.io/)
April 2016 - We created a series of video screencasts, please check them out here: [http://blog.parse.com/learn/parse-server-video-series-april-2016/](http://blog.parse.com/learn/parse-server-video-series-april-2016/)
The fastest and easiest way to get started is to run MongoDB and Parse Server locally.
## Running Parse Server
@@ -28,14 +57,16 @@ $ mongodb-runner start
$ parse-server --appId APPLICATION_ID --masterKey MASTER_KEY --databaseURI mongodb://localhost/test
```
***Note:*** *If installation with* `-g` *fails due to permission problems* (`npm ERR! code 'EACCES'`), *please refer to [this link](https://docs.npmjs.com/getting-started/fixing-npm-permissions).*
### Inside a Docker container
```
$ git clone https://github.com/parse-community/parse-server
$ cd parse-server
$ docker build --tag parse-server .
$ docker run --name my-mongo -d mongo
$ docker run --name my-parse-server --link my-mongo:mongo -d parse-server --appId APPLICATION_ID --masterKey MASTER_KEY --databaseURI mongodb://mongo/test
```
```
You can use any arbitrary string as your application id and master key. These will be used by your clients to authenticate with the Parse Server.
@@ -111,7 +142,7 @@ To learn more about using saving and querying objects on Parse Server, check out
### Connect your app to Parse Server
Parse provides SDKs for all the major platforms. Refer to the Parse Server guide to [learn how to connect your app to Parse Server](https://github.com/parse-community/parse-server/wiki/Parse-Server-Guide#using-parse-sdks-with-parse-server).
Parse provides SDKs for all the major platforms. Refer to the Parse Server guide to [learn how to connect your app to Parse Server](https://docs.parseplatform.org/parse-server/guide/#using-parse-sdks-with-parse-server).
## Running Parse Server elsewhere
@@ -129,7 +160,7 @@ We have provided a basic [Node.js application](https://github.com/parse-communit
* [Digital Ocean](https://www.digitalocean.com/community/tutorials/how-to-run-parse-server-on-ubuntu-14-04)
* [Pivotal Web Services](https://github.com/cf-platform-eng/pws-parse-server)
* [Back4app](http://blog.back4app.com/2016/03/01/quick-wizard-migration/)
* [Gomix](https://gomix.com/#!/project/parse-server)
* [Glitch](https://glitch.com/edit/#!/parse-server)
* [Flynn](https://flynn.io/blog/parse-apps-on-flynn)
### Parse Server + Express
@@ -158,35 +189,15 @@ app.listen(1337, function() {
});
```
For a full list of available options, run `parse-server --help`.
## Logging
Parse Server will, by default, log:
* to the console
* daily rotating files as new line delimited JSON
Logs are also be viewable in Parse Dashboard.
**Want to log each request and response?** Set the `VERBOSE` environment variable when starting `parse-server`. Usage :- `VERBOSE='1' parse-server --appId APPLICATION_ID --masterKey MASTER_KEY`
**Want logs to be in placed in other folder?** Pass the `PARSE_SERVER_LOGS_FOLDER` environment variable when starting `parse-server`. Usage :- `PARSE_SERVER_LOGS_FOLDER='<path-to-logs-folder>' parse-server --appId APPLICATION_ID --masterKey MASTER_KEY`
**Want to log specific levels?** Pass the `logLevel` parameter when starting `parse-server`. Usage :- `parse-server --appId APPLICATION_ID --masterKey MASTER_KEY --logLevel LOG_LEVEL`
**Want new line delimited JSON error logs (for consumption by CloudWatch, Google Cloud Logging, etc.)?** Pass the `JSON_LOGS` environment variable when starting `parse-server`. Usage :- `JSON_LOGS='1' parse-server --appId APPLICATION_ID --masterKey MASTER_KEY`
# Documentation
The full documentation for Parse Server is available in the [wiki](https://github.com/parse-community/parse-server/wiki). The [Parse Server guide](http://docs.parseplatform.org/parse-server/guide/) is a good place to get started. If you're interested in developing for Parse Server, the [Development guide](http://docs.parseplatform.org/parse-server/guide/#development-guide) will help you get set up.
For a full list of available options, run `parse-server --help` or take a look at [Parse Server Configurations](http://parseplatform.org/parse-server/api/master/ParseServerOptions.html).
## Configuration
Parse Server can be configured using the following options. You may pass these as parameters when running a standalone `parse-server`, or by loading a configuration file in JSON format using `parse-server path/to/configuration.json`. If you're using Parse Server on Express, you may also pass these to the `ParseServer` object as options.
For the full list of available options, run `parse-server --help`.
For the full list of available options, run `parse-server --help` or take a look at [Parse Server Configurations](http://parseplatform.org/parse-server/api/master/ParseServerOptions.html).
#### Basic options
### Basic options
* `appId` **(required)** - The application id to host with this server instance. You can use any arbitrary string. For migrated apps, this should match your hosted Parse app.
* `masterKey` **(required)** - The master key to use for overriding ACL security. You can use any arbitrary string. Keep it secret! For migrated apps, this should match your hosted Parse app.
@@ -196,7 +207,7 @@ For the full list of available options, run `parse-server --help`.
* `cloud` - The absolute path to your cloud code `main.js` file.
* `push` - Configuration options for APNS and GCM push. See the [Push Notifications quick start](http://docs.parseplatform.org/parse-server/guide/#push-notifications_push-notifications-quick-start).
#### Client key options
### Client key options
The client keys used with Parse are no longer necessary with Parse Server. If you wish to still require them, perhaps to be able to refuse access to older clients, you can set the keys at initialization time. Setting any of these keys will require all requests to provide one of the configured keys.
@@ -205,36 +216,7 @@ The client keys used with Parse are no longer necessary with Parse Server. If yo
* `restAPIKey`
* `dotNetKey`
#### Advanced options
* `fileKey` - For migrated apps, this is necessary to provide access to files already hosted on Parse.
* `allowClientClassCreation` - Set to false to disable client class creation. Defaults to true.
* `enableAnonymousUsers` - Set to false to disable anonymous users. Defaults to true.
* `auth` - Used to configure support for [3rd party authentication](http://docs.parseplatform.org/parse-server/guide/#oauth-and-3rd-party-authentication).
* `facebookAppIds` - An array of valid Facebook application IDs that users may authenticate with.
* `mountPath` - Mount path for the server. Defaults to `/parse`.
* `filesAdapter` - The default behavior (GridStore) can be changed by creating an adapter class (see [`FilesAdapter.js`](https://github.com/parse-community/parse-server/blob/master/src/Adapters/Files/FilesAdapter.js)).
* `maxUploadSize` - Max file size for uploads. Defaults to 20 MB.
* `loggerAdapter` - The default behavior/transport (File) can be changed by creating an adapter class (see [`LoggerAdapter.js`](https://github.com/parse-community/parse-server/blob/master/src/Adapters/Logger/LoggerAdapter.js)).
* `logLevel` - Set the specific level you want to log. Defaults to `info`. The default logger uses the npm log levels as defined by the underlying winston logger. Check [Winston logging levels](https://github.com/winstonjs/winston#logging-levels) for details on values to specify.
* `sessionLength` - The length of time in seconds that a session should be valid for. Defaults to 31536000 seconds (1 year).
* `revokeSessionOnPasswordReset` - When a user changes their password, either through the reset password email or while logged in, all sessions are revoked if this is true. Set to false if you don't want to revoke sessions.
* `accountLockout` - Lock account when a malicious user is attempting to determine an account password by trial and error.
* `passwordPolicy` - Optional password policy rules to enforce.
* `customPages` - A hash with urls to override email verification links, password reset links and specify frame url for masking user-facing pages. Available keys: `parseFrameURL`, `invalidLink`, `choosePassword`, `passwordResetSuccess`, `verifyEmailSuccess`.
* `middleware` - (CLI only), a module name, function that is an express middleware. When using the CLI, the express app will load it just **before** mounting parse-server on the mount path. This option is useful for injecting a monitoring middleware.
##### Logging
Use the `PARSE_SERVER_LOGS_FOLDER` environment variable when starting `parse-server` to save your server logfiles to the specified folder.
Usage:
```
PARSE_SERVER_LOGS_FOLDER='<path-to-logs-folder>' parse-server --appId APPLICATION_ID --masterKey MASTER_KEY
```
##### Email verification and password reset
### Email verification and password reset
Verifying user email addresses and enabling password reset via email requires an email adapter. As part of the `parse-server` package we provide an adapter for sending email through Mailgun. To use it, sign up for Mailgun, and add this to your initialization code:
@@ -267,7 +249,7 @@ var server = ParseServer({
appName: 'Parse App',
// The email adapter
emailAdapter: {
module: 'parse-server-simple-mailgun-adapter',
module: '@parse/simple-mailgun-adapter',
options: {
// The address that your emails come from
fromAddress: 'parse@example.com',
@@ -286,14 +268,15 @@ var server = ParseServer({
},
// optional settings to enforce password policies
passwordPolicy: {
// Two optional settings to enforce strong passwords. Either one or both can be specified.
// Two optional settings to enforce strong passwords. Either one or both can be specified.
// If both are specified, both checks must pass to accept the password
// 1. a RegExp object or a regex string representing the pattern to enforce
// 1. a RegExp object or a regex string representing the pattern to enforce
validatorPattern: /^(?=.*[a-z])(?=.*[A-Z])(?=.*[0-9])(?=.{8,})/, // enforce password with at least 8 char with at least 1 lower case, 1 upper case and 1 digit
// 2. a callback function to be invoked to validate the password
validatorCallback: (password) => { return validatePassword(password) },
// 2. a callback function to be invoked to validate the password
validatorCallback: (password) => { return validatePassword(password) },
validationError: 'Password must contain at least 1 digit.' // optional error message to be sent instead of the default "Password does not meet the Password Policy requirements." message.
doNotAllowUsername: true, // optional setting to disallow username in passwords
maxPasswordAge: 90, // optional setting in days for password expiry. Login fails if user does not reset the password within this period after signup/last reset.
maxPasswordAge: 90, // optional setting in days for password expiry. Login fails if user does not reset the password within this period after signup/last reset.
maxPasswordHistory: 5, // optional setting to prevent reuse of previous n passwords. Maximum value that can be specified is 20. Not specifying it or specifying 0 will not enforce history.
//optional setting to set a validity duration for password reset links (in seconds)
resetTokenValidityDuration: 24*60*60, // expire after 24 hours
@@ -307,8 +290,10 @@ You can also use other email adapters contributed by the community such as:
- [parse-server-mandrill-adapter](https://www.npmjs.com/package/parse-server-mandrill-adapter)
- [parse-server-simple-ses-adapter](https://www.npmjs.com/package/parse-server-simple-ses-adapter)
- [parse-server-mailgun-adapter-template](https://www.npmjs.com/package/parse-server-mailgun-adapter-template)
- [parse-server-sendinblue-adapter](https://www.npmjs.com/package/parse-server-sendinblue-adapter)
- [parse-server-mailjet-adapter](https://www.npmjs.com/package/parse-server-mailjet-adapter)
- [simple-parse-smtp-adapter](https://www.npmjs.com/package/simple-parse-smtp-adapter)
- [parse-server-generic-email-adapter](https://www.npmjs.com/package/parse-server-generic-email-adapter)
### Using environment variables to configure Parse Server
@@ -320,7 +305,7 @@ PARSE_SERVER_APPLICATION_ID
PARSE_SERVER_MASTER_KEY
PARSE_SERVER_DATABASE_URI
PARSE_SERVER_URL
PARSE_SERVER_CLOUD_CODE_MAIN
PARSE_SERVER_CLOUD
```
The default port is 1337, to use a different port set the PORT environment variable:
@@ -329,24 +314,60 @@ The default port is 1337, to use a different port set the PORT environment varia
$ PORT=8080 parse-server --appId APPLICATION_ID --masterKey MASTER_KEY
```
For the full list of configurable environment variables, run `parse-server --help`.
For the full list of configurable environment variables, run `parse-server --help` or take a look at [Parse Server Configuration](https://github.com/parse-community/parse-server/blob/master/src/Options/Definitions.js).
### Available Adapters
[Parse Server Modules (Adapters)](https://github.com/parse-server-modules)
All official adapters are distributed as scoped pacakges on [npm (@parse)](https://www.npmjs.com/search?q=scope%3Aparse).
Some well maintained adapters are also available on the [Parse Server Modules](https://github.com/parse-server-modules) organization.
You can also find more adapters maintained by the community by searching on [npm](https://www.npmjs.com/search?q=parse-server%20adapter&page=1&ranking=optimal).
### Configuring File Adapters
Parse Server allows developers to choose from several options when hosting files:
* `GridStoreAdapter`, which is backed by MongoDB;
* `GridFSBucketAdapter`, which is backed by MongoDB;
* `S3Adapter`, which is backed by [Amazon S3](https://aws.amazon.com/s3/); or
* `GCSAdapter`, which is backed by [Google Cloud Storage](https://cloud.google.com/storage/)
`GridStoreAdapter` is used by default and requires no setup, but if you're interested in using S3 or Google Cloud Storage, additional configuration information is available in the [Parse Server guide](http://docs.parseplatform.org/parse-server/guide/#configuring-file-adapters).
`GridFSBucketAdapter` is used by default and requires no setup, but if you're interested in using S3 or Google Cloud Storage, additional configuration information is available in the [Parse Server guide](http://docs.parseplatform.org/parse-server/guide/#configuring-file-adapters).
### Logging
Parse Server will, by default, log:
* to the console
* daily rotating files as new line delimited JSON
Logs are also viewable in Parse Dashboard.
**Want to log each request and response?** Set the `VERBOSE` environment variable when starting `parse-server`. Usage :- `VERBOSE='1' parse-server --appId APPLICATION_ID --masterKey MASTER_KEY`
**Want logs to be in placed in a different folder?** Pass the `PARSE_SERVER_LOGS_FOLDER` environment variable when starting `parse-server`. Usage :- `PARSE_SERVER_LOGS_FOLDER='<path-to-logs-folder>' parse-server --appId APPLICATION_ID --masterKey MASTER_KEY`
**Want to log specific levels?** Pass the `logLevel` parameter when starting `parse-server`. Usage :- `parse-server --appId APPLICATION_ID --masterKey MASTER_KEY --logLevel LOG_LEVEL`
**Want new line delimited JSON error logs (for consumption by CloudWatch, Google Cloud Logging, etc)?** Pass the `JSON_LOGS` environment variable when starting `parse-server`. Usage :- `JSON_LOGS='1' parse-server --appId APPLICATION_ID --masterKey MASTER_KEY`
# Live Queries
Live queries are meant to be used in real-time reactive applications, where just using the traditional query paradigm could cause several problems, like increased response time and high network and server usage. Live queries should be used in cases where you need to continuously update a page with fresh data coming from the database, which often happens in (but is not limited to) online games, messaging clients and shared to-do lists.
Take a look at [Live Query Guide](https://docs.parseplatform.org/parse-server/guide/#live-queries), [Live Query Server Setup Guide](https://docs.parseplatform.org/parse-server/guide/#scalability) and [Live Query Protocol Specification](https://github.com/parse-community/parse-server/wiki/Parse-LiveQuery-Protocol-Specification). You can setup a standalone server or multiple instances for scalability (recommended).
# Upgrading to 3.0.0
Starting 3.0.0, parse-server uses the JS SDK version 2.0.
In short, parse SDK v2.0 removes the backbone style callbacks as well as the Parse.Promise object in favor of native promises.
All the Cloud Code interfaces also have been updated to reflect those changes, and all backbone style response objects are removed and replaced by Promise style resolution.
We have written up a [migration guide](3.0.0.md), hoping this will help you transition to the next major release.
# Support
For implementation related questions or technical support, please refer to the [Stack Overflow](http://stackoverflow.com/questions/tagged/parse.com) and [Server Fault](https://serverfault.com/tags/parse) communities.
For implementation related questions or any other questions please refer to [Parse Community's Discourse forum](https://community.parseplatform.org/c/parse-server).
If you believe you've found an issue with Parse Server, make sure these boxes are checked before [reporting an issue](https://github.com/parse-community/parse-server/issues):
@@ -358,20 +379,99 @@ If you believe you've found an issue with Parse Server, make sure these boxes ar
# Want to ride the bleeding edge?
The `latest` branch in this repository is automatically maintained to be the last
commit to `master` to pass all tests, in the same form found on npm. It is
recommend to use builds deployed npm for many reasons, but if you want to use
It is recommend to use builds deployed npm for many reasons, but if you want to use
the latest not-yet-released version of parse-server, you can do so by depending
directly on this branch:
```
npm install parseplatform/parse-server.git#latest
npm install parse-community/parse-server.git#master
```
## Experimenting
You can also use your own forks, and work in progress branches by specifying them:
```
npm install github:myUsername/parse-server#my-awesome-feature
```
And don't forget, if you plan to deploy it remotely, you should run `npm install` with the `--save` option.
# Contributing
We really want Parse to be yours, to see it grow and thrive in the open source community. Please see the [Contributing to Parse Server guide](CONTRIBUTING.md).
# Backers
Support us with a monthly donation and help us continue our activities. [[Become a backer](https://opencollective.com/parse-server#backer)]
<a href="https://opencollective.com/parse-server/backer/0/website" target="_blank"><img src="https://opencollective.com/parse-server/backer/0/avatar.svg"></a>
<a href="https://opencollective.com/parse-server/backer/1/website" target="_blank"><img src="https://opencollective.com/parse-server/backer/1/avatar.svg"></a>
<a href="https://opencollective.com/parse-server/backer/2/website" target="_blank"><img src="https://opencollective.com/parse-server/backer/2/avatar.svg"></a>
<a href="https://opencollective.com/parse-server/backer/3/website" target="_blank"><img src="https://opencollective.com/parse-server/backer/3/avatar.svg"></a>
<a href="https://opencollective.com/parse-server/backer/4/website" target="_blank"><img src="https://opencollective.com/parse-server/backer/4/avatar.svg"></a>
<a href="https://opencollective.com/parse-server/backer/5/website" target="_blank"><img src="https://opencollective.com/parse-server/backer/5/avatar.svg"></a>
<a href="https://opencollective.com/parse-server/backer/6/website" target="_blank"><img src="https://opencollective.com/parse-server/backer/6/avatar.svg"></a>
<a href="https://opencollective.com/parse-server/backer/7/website" target="_blank"><img src="https://opencollective.com/parse-server/backer/7/avatar.svg"></a>
<a href="https://opencollective.com/parse-server/backer/8/website" target="_blank"><img src="https://opencollective.com/parse-server/backer/8/avatar.svg"></a>
<a href="https://opencollective.com/parse-server/backer/9/website" target="_blank"><img src="https://opencollective.com/parse-server/backer/9/avatar.svg"></a>
<a href="https://opencollective.com/parse-server/backer/10/website" target="_blank"><img src="https://opencollective.com/parse-server/backer/10/avatar.svg"></a>
<a href="https://opencollective.com/parse-server/backer/11/website" target="_blank"><img src="https://opencollective.com/parse-server/backer/11/avatar.svg"></a>
<a href="https://opencollective.com/parse-server/backer/12/website" target="_blank"><img src="https://opencollective.com/parse-server/backer/12/avatar.svg"></a>
<a href="https://opencollective.com/parse-server/backer/13/website" target="_blank"><img src="https://opencollective.com/parse-server/backer/13/avatar.svg"></a>
<a href="https://opencollective.com/parse-server/backer/14/website" target="_blank"><img src="https://opencollective.com/parse-server/backer/14/avatar.svg"></a>
<a href="https://opencollective.com/parse-server/backer/15/website" target="_blank"><img src="https://opencollective.com/parse-server/backer/15/avatar.svg"></a>
<a href="https://opencollective.com/parse-server/backer/16/website" target="_blank"><img src="https://opencollective.com/parse-server/backer/16/avatar.svg"></a>
<a href="https://opencollective.com/parse-server/backer/17/website" target="_blank"><img src="https://opencollective.com/parse-server/backer/17/avatar.svg"></a>
<a href="https://opencollective.com/parse-server/backer/18/website" target="_blank"><img src="https://opencollective.com/parse-server/backer/18/avatar.svg"></a>
<a href="https://opencollective.com/parse-server/backer/19/website" target="_blank"><img src="https://opencollective.com/parse-server/backer/19/avatar.svg"></a>
<a href="https://opencollective.com/parse-server/backer/20/website" target="_blank"><img src="https://opencollective.com/parse-server/backer/20/avatar.svg"></a>
<a href="https://opencollective.com/parse-server/backer/21/website" target="_blank"><img src="https://opencollective.com/parse-server/backer/21/avatar.svg"></a>
<a href="https://opencollective.com/parse-server/backer/22/website" target="_blank"><img src="https://opencollective.com/parse-server/backer/22/avatar.svg"></a>
<a href="https://opencollective.com/parse-server/backer/23/website" target="_blank"><img src="https://opencollective.com/parse-server/backer/23/avatar.svg"></a>
<a href="https://opencollective.com/parse-server/backer/24/website" target="_blank"><img src="https://opencollective.com/parse-server/backer/24/avatar.svg"></a>
<a href="https://opencollective.com/parse-server/backer/25/website" target="_blank"><img src="https://opencollective.com/parse-server/backer/25/avatar.svg"></a>
<a href="https://opencollective.com/parse-server/backer/26/website" target="_blank"><img src="https://opencollective.com/parse-server/backer/26/avatar.svg"></a>
<a href="https://opencollective.com/parse-server/backer/27/website" target="_blank"><img src="https://opencollective.com/parse-server/backer/27/avatar.svg"></a>
<a href="https://opencollective.com/parse-server/backer/28/website" target="_blank"><img src="https://opencollective.com/parse-server/backer/28/avatar.svg"></a>
<a href="https://opencollective.com/parse-server/backer/29/website" target="_blank"><img src="https://opencollective.com/parse-server/backer/29/avatar.svg"></a>
# Sponsors
Become a sponsor and get your logo on our README on Github with a link to your site. [[Become a sponsor](https://opencollective.com/parse-server#sponsor)]
<a href="https://opencollective.com/parse-server/sponsor/0/website" target="_blank"><img src="https://opencollective.com/parse-server/sponsor/0/avatar.svg"></a>
<a href="https://opencollective.com/parse-server/sponsor/1/website" target="_blank"><img src="https://opencollective.com/parse-server/sponsor/1/avatar.svg"></a>
<a href="https://opencollective.com/parse-server/sponsor/2/website" target="_blank"><img src="https://opencollective.com/parse-server/sponsor/2/avatar.svg"></a>
<a href="https://opencollective.com/parse-server/sponsor/3/website" target="_blank"><img src="https://opencollective.com/parse-server/sponsor/3/avatar.svg"></a>
<a href="https://opencollective.com/parse-server/sponsor/4/website" target="_blank"><img src="https://opencollective.com/parse-server/sponsor/4/avatar.svg"></a>
<a href="https://opencollective.com/parse-server/sponsor/5/website" target="_blank"><img src="https://opencollective.com/parse-server/sponsor/5/avatar.svg"></a>
<a href="https://opencollective.com/parse-server/sponsor/6/website" target="_blank"><img src="https://opencollective.com/parse-server/sponsor/6/avatar.svg"></a>
<a href="https://opencollective.com/parse-server/sponsor/7/website" target="_blank"><img src="https://opencollective.com/parse-server/sponsor/7/avatar.svg"></a>
<a href="https://opencollective.com/parse-server/sponsor/8/website" target="_blank"><img src="https://opencollective.com/parse-server/sponsor/8/avatar.svg"></a>
<a href="https://opencollective.com/parse-server/sponsor/9/website" target="_blank"><img src="https://opencollective.com/parse-server/sponsor/9/avatar.svg"></a>
<a href="https://opencollective.com/parse-server/sponsor/10/website" target="_blank"><img src="https://opencollective.com/parse-server/sponsor/10/avatar.svg"></a>
<a href="https://opencollective.com/parse-server/sponsor/11/website" target="_blank"><img src="https://opencollective.com/parse-server/sponsor/11/avatar.svg"></a>
<a href="https://opencollective.com/parse-server/sponsor/12/website" target="_blank"><img src="https://opencollective.com/parse-server/sponsor/12/avatar.svg"></a>
<a href="https://opencollective.com/parse-server/sponsor/13/website" target="_blank"><img src="https://opencollective.com/parse-server/sponsor/13/avatar.svg"></a>
<a href="https://opencollective.com/parse-server/sponsor/14/website" target="_blank"><img src="https://opencollective.com/parse-server/sponsor/14/avatar.svg"></a>
<a href="https://opencollective.com/parse-server/sponsor/15/website" target="_blank"><img src="https://opencollective.com/parse-server/sponsor/15/avatar.svg"></a>
<a href="https://opencollective.com/parse-server/sponsor/16/website" target="_blank"><img src="https://opencollective.com/parse-server/sponsor/16/avatar.svg"></a>
<a href="https://opencollective.com/parse-server/sponsor/17/website" target="_blank"><img src="https://opencollective.com/parse-server/sponsor/17/avatar.svg"></a>
<a href="https://opencollective.com/parse-server/sponsor/18/website" target="_blank"><img src="https://opencollective.com/parse-server/sponsor/18/avatar.svg"></a>
<a href="https://opencollective.com/parse-server/sponsor/19/website" target="_blank"><img src="https://opencollective.com/parse-server/sponsor/19/avatar.svg"></a>
<a href="https://opencollective.com/parse-server/sponsor/20/website" target="_blank"><img src="https://opencollective.com/parse-server/sponsor/20/avatar.svg"></a>
<a href="https://opencollective.com/parse-server/sponsor/21/website" target="_blank"><img src="https://opencollective.com/parse-server/sponsor/21/avatar.svg"></a>
<a href="https://opencollective.com/parse-server/sponsor/22/website" target="_blank"><img src="https://opencollective.com/parse-server/sponsor/22/avatar.svg"></a>
<a href="https://opencollective.com/parse-server/sponsor/23/website" target="_blank"><img src="https://opencollective.com/parse-server/sponsor/23/avatar.svg"></a>
<a href="https://opencollective.com/parse-server/sponsor/24/website" target="_blank"><img src="https://opencollective.com/parse-server/sponsor/24/avatar.svg"></a>
<a href="https://opencollective.com/parse-server/sponsor/25/website" target="_blank"><img src="https://opencollective.com/parse-server/sponsor/25/avatar.svg"></a>
<a href="https://opencollective.com/parse-server/sponsor/26/website" target="_blank"><img src="https://opencollective.com/parse-server/sponsor/26/avatar.svg"></a>
<a href="https://opencollective.com/parse-server/sponsor/27/website" target="_blank"><img src="https://opencollective.com/parse-server/sponsor/27/avatar.svg"></a>
<a href="https://opencollective.com/parse-server/sponsor/28/website" target="_blank"><img src="https://opencollective.com/parse-server/sponsor/28/avatar.svg"></a>
<a href="https://opencollective.com/parse-server/sponsor/29/website" target="_blank"><img src="https://opencollective.com/parse-server/sponsor/29/avatar.svg"></a>
-----
As of April 5, 2017, Parse, LLC has transferred this code to the parse-community organization, and will no longer be contributing to or distributing this code.
+75
View File
@@ -0,0 +1,75 @@
# Parse Community Vulnerability Disclosure Program
If you believe you have found a security vulnerability on one of parse-community maintained packages,
we encourage you to let us know right away.
We will investigate all legitimate reports and do our best to quickly fix the problem.
Before reporting though, please review this page including and those things that should not be reported.
# Responsible Disclosure Policy
If you comply with the policies below when reporting a security issue to parse community,
we will not initiate a lawsuit or law enforcement investigation against you in response to your report.
We ask that:
- You give us reasonable time to investigate and mitigate an issue you report before making public any information about the report or sharing such information with others. This means we request _at least_ **7 days** to get back to you with an initial response and _at least_ **30 days** from initial contact (made by you) to apply a patch.
- You do not interact with an individual account (which includes modifying or accessing data from the account) if the account owner has not consented to such actions.
- You make a good faith effort to avoid privacy violations and disruptions to others, including (but not limited to) destruction of data and interruption or degradation of our services.
- You do not exploit a security issue you discover for any reason. (This includes demonstrating additional risk, such as attempted compromise of sensitive company data or probing for additional issues). You do not violate any other applicable laws or regulations.
# Communicating with us
All vulnerability should be privately reported to either [Node Security](https://nodesecurity.io/report) or directly to us at the following address [security at parseplatform dot org](mailto:security@parseplatform.org)
You can use our PGP public key, which is also uploaded [here](hkp://pgp.mit.edu):
```
-----BEGIN PGP PUBLIC KEY BLOCK-----
mQINBFoi200BEACnFHa4Atvw62TjpI5uDtyOF1Ab6gd6898ITXlzFVlAoiqTBE2o
S3H9vCe6w32HkTqyKiymdk50mAaDJrEOyAZSqj0gc4r7vmCx2s7f3iO9A9PEGsHj
UROnkJ5v2su1Dk95XQrbrR4JyNvFMLLqEbdK78Mhx/Xd5QqOD8pop0cS8pF1f1Mb
3MiZb3bxFj+7n+KC80C2+CNyJt95alVnq2MuwbEuwTJQV5CEgRqzBCov9qnLgloK
w7YP4YLkKZoMZQ45mWCUTmn8YIa9PabDLXUhlKv3MQInhnJIELb+jSKO96glHr6p
DpFf0pwRfsuoIhy3jaO7K/ws5uZY3/Ae3gjrAlOB8jhae0POWSwEM+iWHg3wcjpt
lRdu/OgPXqKIgMAXw6Kx9XrskEhOI9ZQfHZlK3HL4ArSdtGYIO5pVNeRssljJvJm
G5HJuGBaPCQNYX7BWJkXFF0HYV6Ke2JDXGVSM4ubPJsZcA4Yx7SYyvuOsNgqHOFM
9snfPfAPAhu/4zCYNCO2NNBc7HaH+qwIvveWX5tTGe6UpX/wOcD8xkoEn+UygQbO
lwu+kjFn8H6RlDChPR65aJTU5Lu0kqRnej1gCjYzOS6AjFOjLuRBlRaey+myhQHO
TbII9nkYI3abYhD8Di77Ve6XFMQI1grPGaqNp6ZLfej1u8PExpclzxTbtQARAQAB
tDdQYXJzZVBsYXRmb3JtLm9yZyBTZWN1cml0eSA8c2VjdXJpdHlAcGFyc2VwbGF0
Zm9ybS5vcmc+iQI9BBMBCgAnBQJaIttNAhsDBQkHhh+ABQsJCAcDBRUKCQgLBRYC
AwEAAh4BAheAAAoJEOaNxtHMZ3/L13EP/2X4i0h1Jidr0GN6t7LUJFDBDHxnY+V+
kPXuucWqtFSkiznHNWSKh8N1LY9N+5eYQj21fJw2RV+ePZKb07jcThG8G1qg+tIn
bbLO2RxmuO/ISgqgvpmtZH9FHtjpGaGStCQqljtZDcBI0Y52l9SLItZjoiRlXePb
C+embDhO6Wzgsi+zkzskm8ISw4mTbDY+HN/1TakCsfMMs8J7twQar+eW97WibzRW
pvhIeJ6egzxs2dW0bXxb0OPvx9xm/FxxRgf+2vySYFnZ88j7Luur4VKdBvh1NtUT
n+xLp/heCHQ/If1ou1Dd6rypEng3A75WANf5m1TAd8JrlQZEOLm4oW+brpDBamqc
dP2z2/zzKykZzRoN1PSX9JRsyLhJhZSXIm/49gATtkaUFBBsFrrwqdWTV7tWFmQI
Oe2wT9IExe+RmXsVqwIndyVkcROuXXGt3y92dcmEriNUGmYXJchs9pAdOCkpHqmf
BkZWalXuWdWiwshNiEF3KWiNbw6BMEWlKPuuJ6kWevLj35MQMLIIlGLx722/kbBB
KIJfp8NdWncIyx0ulq70GWdNaKvu+9dDElMCsPqXeVxbXVpGiBcIgX1S0JEU0YX7
kShAAkZDwhyfUh4IYbyJbNVNzhMazDqI48kP8Kz8vqvZAf3SXyjSBL1XKWLAfKtb
Bs4OuHR3FDkjuQINBFoi200BEADoh8xHW4SVuVnF+Y82Z2B25Ybhwd13mP3LUxwX
cRYeyVge8V7kyftYiUbIybDoxa3H8ysiSgH51yHkoRsq8JO3WEnJZop1ZnjOKhjv
g73vciKImAGrsTVtUR4ErwUsHaXgCFmlmyTI/RLPDfvHdZyYm9wji4XLZrFu5O/s
JD1wU1/aMYCBRjrszI4uklyqGUx2kKLYEI7iITAVVQ8/xFlPPYhVX02jR7fCqTkI
ualVAzK/6Lc4VGqjzFG5kElLCIVDTMLJvPETgXeSAuBigcC+osTw8IRhqY87qNbn
ZpGDKJxV7ARXg5akg/98xI1TZiAMP1vHVKke7XiH5E6M/3IyPiY5ns4SaXu3ZonN
EUR9Z60IeUHVkGpMUk0K0avkaLIAWb93D6jlZ+fJoNBuCoWB31d6+/W8LnrocTQb
mL9ls8HiabrPDsuzI9y3ptOjgGn8pmWoJHM2fiQ5eElexHFB+1QTqq1yiTnuohi/
p+BbKK6V/FZbrP4Rd3hOmPkOWag3KvwGFF8vW6gmx3lft+B90TtBSMv46lqDI5TX
VUr3Un9YHIF+xQMNAgZuJkTIVLrYgexRHnJGrwhRXSZgpVkvj/ygIhR2EvY8zySh
/EFw9ZTmhOrR34q/qlChEuUriP1cc1ygTnjfcw+ZgsTPh50VZk8s5cZAa8tVFINR
cZKBSwARAQABiQIlBBgBCgAPBQJaIttNAhsMBQkHhh+AAAoJEOaNxtHMZ3/LgbUQ
AJHyhtH3bkQsWH0Z+PonbcNxZNq1ZlOfosQIWRzX8BioJpQ5qttkKt5PwhBDrr9h
4ySYzsbTg3fSkIkjfUfaRr+62xay5efaIwG5PcHdXOR/fKCJ6NrTvMEzPUZ8521e
yk0rjm++bNqh+Knez0+xnk99n1XlPrMAXrEb63oM5IBnpJC1PcztzMhdvsNw+B/l
2YcVhXH2peOp/GbAd58fB7JMggwumCktFnXYsYZlMFch3mwKDb66Qi2gbCgK2KHU
sj/mPag7vpy1E4lOlBnDeZpYnjfE/8VJkT4ck5OAwywZK/NUqLfh35RaIwjeXDLM
nlLff3HQJPXYzavCzLZ5dMZJfn0968NIHitjoW9VLs9UhrY7EWI7T6GAMX3wHcq6
ssGOkS0Y1OW8s7jFuoe00PByjiCHcFjBG2NF3n08Nu2c5hewGPs0FdhTadQtHpI2
TTeSIxQ2Ui21UfcX4wMbqELh871ZeQcbVp5LWWibVbfy4mx4Tq/Hvgp7DeBh8DLF
/7MDDwZ+RIBoy98CYz4xsFMdS/9L64uBk/0C+U4OwFJI1FDDxFp6cDqjxdykWi48
wsCczfashguiuJeJ1Ug8URRLY/DKQmjSJaCwy3McK/MOb+JVMazMUyrU9XaGuP4Y
Co6fHPyjrvmE5DtU5Vp8O68ZpOYrkM6X22dIQpPi6atm
=o7Nx
-----END PGP PUBLIC KEY BLOCK-----
```
-37
View File
@@ -1,37 +0,0 @@
#!/usr/bin/env node
var nodemon = require('nodemon');
var babel = require("babel-core");
var gaze = require('gaze');
var fs = require('fs');
var path = require('path');
// Watch the src and transpile when changed
gaze('src/**/*', function(err, watcher) {
if (err) throw err;
watcher.on('changed', function(sourceFile) {
console.log(sourceFile + " has changed");
try {
targetFile = path.relative(__dirname, sourceFile).replace(/\/src\//, '/lib/');
targetFile = path.resolve(__dirname, targetFile);
fs.writeFile(targetFile, babel.transformFileSync(sourceFile).code);
} catch (e) {
console.error(e.message, e.stack);
}
});
});
try {
// Run and watch dist
nodemon({
script: 'bin/parse-server',
ext: 'js json',
watch: 'lib'
});
} catch (e) {
console.error(e.message, e.stack);
}
process.once('SIGINT', function() {
process.exit(0);
});
+23
View File
@@ -0,0 +1,23 @@
{
"plugins": ["node_modules/jsdoc-babel", "plugins/markdown"],
"babel": {
"plugins": ["@babel/plugin-transform-flow-strip-types"]
},
"source": {
"include": ["./README.md", "./src/cloud-code", "./src/Options/docs.js", "./src/ParseServer.js", "./src/Adapters"],
"excludePattern": "(^|\\/|\\\\)_"
},
"templates": {
"default": {
"outputSourceFiles": false,
"showInheritedInNav": false,
"useLongnameInNav": true
},
"cleverLinks": true,
"monospaceLinks": false
},
"opts": {
"template": "node_modules/@parse/minami",
"recurse": true
}
}
+10359
View File
File diff suppressed because it is too large Load Diff
+75 -52
View File
@@ -1,11 +1,11 @@
{
"name": "parse-server",
"version": "2.5.1",
"version": "3.4.4",
"description": "An express module providing a Parse-compatible API server",
"main": "lib/index.js",
"repository": {
"type": "git",
"url": "https://github.com/ParsePlatform/parse-server"
"url": "https://github.com/parse-community/parse-server"
},
"files": [
"bin/",
@@ -14,77 +14,100 @@
"views/",
"LICENSE",
"PATENTS",
"postinstall.js",
"README.md"
],
"license": "BSD-3-Clause",
"dependencies": {
"@parse/fs-files-adapter": "1.0.1",
"@parse/push-adapter": "3.0.0",
"@parse/s3-files-adapter": "1.2.1",
"@parse/simple-mailgun-adapter": "1.1.0",
"bcryptjs": "2.4.3",
"body-parser": "1.17.2",
"commander": "2.10.0",
"deepcopy": "0.6.3",
"express": "4.15.3",
"body-parser": "1.19.0",
"commander": "2.20.0",
"deepcopy": "2.0.0",
"express": "4.17.0",
"follow-redirects": "1.7.0",
"intersect": "1.0.1",
"lodash": "4.17.4",
"lru-cache": "4.1.1",
"mime": "1.3.6",
"mongodb": "2.2.28",
"multer": "1.3.0",
"parse": "1.10.0",
"parse-server-fs-adapter": "1.0.1",
"parse-server-push-adapter": "2.0.0",
"parse-server-s3-adapter": "1.1.0",
"parse-server-simple-mailgun-adapter": "1.0.0",
"pg-promise": "6.3.0",
"redis": "2.7.1",
"request": "2.81.0",
"semver": "5.3.0",
"lodash": "4.17.11",
"lru-cache": "5.1.1",
"mime": "2.4.3",
"mongodb": "3.2.5",
"parse": "2.4.0",
"pg-promise": "8.7.2",
"redis": "2.8.0",
"semver": "6.0.0",
"tv4": "1.3.0",
"winston": "2.3.1",
"winston-daily-rotate-file": "1.4.6",
"ws": "3.0.0"
"uuid": "3.3.2",
"winston": "3.2.1",
"winston-daily-rotate-file": "3.9.0",
"ws": "7.0.0"
},
"devDependencies": {
"babel-cli": "6.24.1",
"babel-core": "6.25.0",
"babel-eslint": "^7.1.1",
"babel-plugin-syntax-flow": "6.18.0",
"babel-plugin-transform-flow-strip-types": "6.22.0",
"babel-preset-es2015": "6.24.1",
"babel-preset-stage-0": "6.24.1",
"babel-register": "6.24.1",
"@babel/cli": "7.4.4",
"@babel/core": "7.4.5",
"@babel/plugin-proposal-object-rest-spread": "7.4.4",
"@babel/plugin-transform-flow-strip-types": "7.4.4",
"@babel/preset-env": "7.4.5",
"@parse/minami": "1.0.0",
"babel-eslint": "10.0.0",
"bcrypt-nodejs": "0.0.3",
"cross-env": "5.0.1",
"deep-diff": "0.3.8",
"eslint": "^4.0.0",
"eslint-plugin-flowtype": "^2.25.0",
"gaze": "1.1.2",
"jasmine": "2.6.0",
"jasmine-spec-reporter": "^4.1.0",
"mongodb-runner": "3.5.0",
"nodemon": "1.11.0",
"nyc": "^11.0.2",
"request-promise": "4.2.1"
"cross-env": "5.2.0",
"deep-diff": "1.0.2",
"eslint": "5.16.0",
"eslint-plugin-flowtype": "3.9.0",
"flow-bin": "0.98.1",
"gaze": "1.1.3",
"husky": "2.3.0",
"jasmine": "3.4.0",
"jasmine-spec-reporter": "4.2.1",
"jsdoc": "3.6.2",
"jsdoc-babel": "0.5.0",
"lint-staged": "8.1.7",
"mongodb-runner": "4.3.2",
"nyc": "14.1.1",
"prettier": "1.17.1",
"supports-color": "6.0.0"
},
"scripts": {
"definitions": "node ./resources/buildConfigDefinitions.js",
"docs": "jsdoc -c ./jsdoc-conf.json",
"dev": "npm run build && node bin/dev",
"lint": "eslint --cache ./",
"lint": "flow && eslint --cache ./",
"build": "babel src/ -d lib/ --copy-files",
"pretest": "npm run lint",
"test": "cross-env MONGODB_VERSION=${MONGODB_VERSION:=3.2.6} MONGODB_STORAGE_ENGINE=mmapv1 TESTING=1 $COVERAGE_OPTION jasmine",
"test:win": "cross-env MONGODB_VERSION=${MONGODB_VERSION:=3.2.6} MONGODB_STORAGE_ENGINE=mmapv1 TESTING=1 jasmine",
"coverage": "cross-env COVERAGE_OPTION='./node_modules/.bin/nyc' npm test",
"coverage:win": "cross-env MONGODB_VERSION=${MONGODB_VERSION:=3.2.6} MONGODB_STORAGE_ENGINE=mmapv1 TESTING=1 node ./node_modules/.bin/nyc ./node_modules/jasmine/bin/jasmine.js",
"watch": "babel --watch src/ -d lib/ --copy-files",
"test": "cross-env MONGODB_VERSION=${MONGODB_VERSION:=4.0.4} MONGODB_STORAGE_ENGINE=mmapv1 TESTING=1 jasmine",
"coverage": "cross-env MONGODB_VERSION=${MONGODB_VERSION:=4.0.4} MONGODB_STORAGE_ENGINE=mmapv1 TESTING=1 nyc jasmine",
"start": "node ./bin/parse-server",
"prepublish": "npm run build"
"prepare": "npm run build",
"postinstall": "node -p 'require(\"./postinstall.js\")()'"
},
"engines": {
"node": ">=4.6"
"node": ">= 8"
},
"bin": {
"parse-server": "./bin/parse-server"
},
"optionalDependencies": {
"bcrypt": "1.0.2",
"uws": "^0.14.5"
"bcrypt": "3.0.6",
"uws": "10.148.1"
},
"collective": {
"type": "opencollective",
"url": "https://opencollective.com/parse-server",
"logo": "https://opencollective.com/parse-server/logo.txt?reverse=true&variant=binary"
},
"husky": {
"hooks": {
"pre-commit": "lint-staged"
}
},
"lint-staged": {
"{src,spec}/**/*.js": [
"prettier --write",
"eslint --cache",
"git add"
]
}
}
+50
View File
@@ -0,0 +1,50 @@
const pkg = require('./package.json');
const version = parseFloat(process.version.substr(1));
const minimum = parseFloat(pkg.engines.node.match(/\d+/g).join('.'));
module.exports = function () {
const openCollective = `
1111111111
1111111111111111
1111111111111111111111
11111111111111111111111111
111111111111111 11111111
1111111111111 111111
1111111111111 111111111 111111
111111111111 11111111111 111111
1111111111111 11111111111 111111
1111111111111 1111111111 111111
1111111111111111111111111 1111111
11111111 11111111
111111 1111111111111111111
11111 11111 111111111111111111
11111 11111111111111111
111111 111111111111111111
11111111111111111111111111
1111111111111111111111
111111111111111111
11111111111
Thanks for installing parse 🙏
Please consider donating to our open collective
to help us maintain this package.
👉 https://opencollective.com/parse-server
`;
process.stdout.write(openCollective);
if (version >= minimum) {
process.exit(0);
}
const errorMessage = `
⚠️ parse-server requires at least node@${minimum}!
You have node@${version}
`;
process.stdout.write(errorMessage);
process.exit(1);
};
+29
View File
@@ -0,0 +1,29 @@
#!/bin/sh -e
set -x
if [ "${TRAVIS_REPO_SLUG}" = "" ];
then
echo "Cannot release docs without TRAVIS_REPO_SLUG set"
exit 0;
fi
REPO="https://github.com/${TRAVIS_REPO_SLUG}"
rm -rf docs
git clone -b gh-pages --single-branch $REPO ./docs
cd docs
git pull origin gh-pages
cd ..
DEST="master"
if [ "${TRAVIS_TAG}" != "" ];
then
DEST="${TRAVIS_TAG}"
# change the default page to the latest
echo "<meta http-equiv='refresh' content='0; url=/parse-server/api/${DEST}'>" > "docs/api/index.html"
fi
npm run definitions
npm run docs
mkdir -p "docs/api/${DEST}"
cp -R out/* "docs/api/${DEST}"
+276
View File
@@ -0,0 +1,276 @@
/**
* Parse Server Configuration Builder
*
* This module builds the definitions file (src/Options/Definitions.js)
* from the src/Options/index.js options interfaces.
* The Definitions.js module is responsible for the default values as well
* as the mappings for the CLI.
*
* To rebuild the definitions file, run
* `$ node resources/buildConfigDefinitions.js`
*/
const parsers = require('../src/Options/parsers');
function last(array) {
return array[array.length - 1];
}
const letters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
function toENV(key) {
let str = '';
let previousIsUpper = false;
for(let i = 0; i < key.length; i++) {
const char = key[i];
if (letters.indexOf(char) >= 0) {
if (!previousIsUpper) {
str += '_';
previousIsUpper = true;
}
} else {
previousIsUpper = false;
}
str += char;
}
return str.toUpperCase();
}
function getCommentValue(comment) {
if (!comment) { return }
return comment.value.trim();
}
function getENVPrefix(iface) {
if (iface.id.name === 'ParseServerOptions') {
return 'PARSE_SERVER_';
}
if (iface.id.name === 'CustomPagesOptions') {
return 'PARSE_SERVER_CUSTOM_PAGES_';
}
if (iface.id.name === 'LiveQueryServerOptions') {
return 'PARSE_LIVE_QUERY_SERVER_';
}
if (iface.id.name === 'LiveQueryOptions') {
return 'PARSE_SERVER_LIVEQUERY_';
}
}
function processProperty(property, iface) {
const firstComment = getCommentValue(last(property.leadingComments || []));
const name = property.key.name;
const prefix = getENVPrefix(iface);
if (!firstComment) {
return;
}
const lines = firstComment.split('\n').map((line) => line.trim());
let help = '';
let envLine;
let defaultLine;
lines.forEach((line) => {
if (line.indexOf(':ENV:') === 0) {
envLine = line;
} else if (line.indexOf(':DEFAULT:') === 0) {
defaultLine = line;
} else {
help += line;
}
});
let env;
if (envLine) {
env = envLine.split(' ')[1];
} else {
env = (prefix + toENV(name));
}
let defaultValue;
if (defaultLine) {
defaultValue = defaultLine.split(' ')[1];
}
let type = property.value.type;
let isRequired = true;
if (type == 'NullableTypeAnnotation') {
isRequired = false;
type = property.value.typeAnnotation.type;
}
return {
name,
env,
help,
type,
defaultValue,
types: property.value.types,
typeAnnotation: property.value.typeAnnotation,
required: isRequired
};
}
function doInterface(iface) {
return iface.body.properties
.sort((a, b) => a.key.name.localeCompare(b.key.name))
.map((prop) => processProperty(prop, iface))
.filter((e) => e !== undefined);
}
function mapperFor(elt, t) {
const p = t.identifier('parsers');
const wrap = (identifier) => t.memberExpression(p, identifier);
if (t.isNumberTypeAnnotation(elt)) {
return t.callExpression(wrap(t.identifier('numberParser')), [t.stringLiteral(elt.name)]);
} else if (t.isArrayTypeAnnotation(elt)) {
return wrap(t.identifier('arrayParser'));
} else if (t.isAnyTypeAnnotation(elt)) {
return wrap(t.identifier('objectParser'));
} else if (t.isBooleanTypeAnnotation(elt)) {
return wrap(t.identifier('booleanParser'));
} else if (t.isGenericTypeAnnotation(elt)) {
const type = elt.typeAnnotation.id.name;
if (type == 'Adapter') {
return wrap(t.identifier('moduleOrObjectParser'));
}
if (type == 'NumberOrBoolean') {
return wrap(t.identifier('numberOrBooleanParser'));
}
return wrap(t.identifier('objectParser'));
}
}
function parseDefaultValue(elt, value, t) {
let literalValue;
if (t.isStringTypeAnnotation(elt)) {
if (value == '""' || value == "''") {
literalValue = t.stringLiteral('');
} else {
literalValue = t.stringLiteral(value);
}
} else if (t.isNumberTypeAnnotation(elt)) {
literalValue = t.numericLiteral(parsers.numberOrBoolParser('')(value));
} else if (t.isArrayTypeAnnotation(elt)) {
const array = parsers.objectParser(value);
literalValue = t.arrayExpression(array.map((value) => {
if (typeof value == 'string') {
return t.stringLiteral(value);
} else {
throw new Error('Unable to parse array');
}
}));
} else if (t.isAnyTypeAnnotation(elt)) {
literalValue = t.arrayExpression([]);
} else if (t.isBooleanTypeAnnotation(elt)) {
literalValue = t.booleanLiteral(parsers.booleanParser(value));
} else if (t.isGenericTypeAnnotation(elt)) {
const type = elt.typeAnnotation.id.name;
if (type == 'NumberOrBoolean') {
literalValue = t.numericLiteral(parsers.numberOrBoolParser('')(value));
}
if (type == 'CustomPagesOptions') {
const object = parsers.objectParser(value);
const props = Object.keys(object).map((key) => {
return t.objectProperty(key, object[value]);
});
literalValue = t.objectExpression(props);
}
if (type == 'ProtectedFields') {
const prop = t.objectProperty(
t.stringLiteral('_User'), t.objectPattern([
t.objectProperty(t.stringLiteral('*'), t.arrayExpression([t.stringLiteral('email')]))
])
);
literalValue = t.objectExpression([prop]);
}
}
return literalValue;
}
function inject(t, list) {
let comments = '';
const results = list.map((elt) => {
if (!elt.name) {
return;
}
const props = ['env', 'help'].map((key) => {
if (elt[key]) {
return t.objectProperty(t.stringLiteral(key), t.stringLiteral(elt[key]));
}
}).filter((e) => e !== undefined);
if (elt.required) {
props.push(t.objectProperty(t.stringLiteral('required'), t.booleanLiteral(true)))
}
const action = mapperFor(elt, t);
if (action) {
props.push(t.objectProperty(t.stringLiteral('action'), action))
}
if (elt.defaultValue) {
const parsedValue = parseDefaultValue(elt, elt.defaultValue, t);
if (parsedValue) {
props.push(t.objectProperty(t.stringLiteral('default'), parsedValue));
} else {
throw new Error(`Unable to parse value for ${elt.name} `);
}
}
let type = elt.type.replace('TypeAnnotation', '');
if (type === 'Generic') {
type = elt.typeAnnotation.id.name;
}
if (type === 'Array') {
type = `${elt.typeAnnotation.elementType.type.replace('TypeAnnotation', '')}[]`;
}
if (type === 'NumberOrBoolean') {
type = 'Number|Boolean';
}
if (type === 'Adapter') {
const adapterType = elt.typeAnnotation.typeParameters.params[0].id.name;
type = `Adapter<${adapterType}>`;
}
comments += ` * @property {${type}} ${elt.name} ${elt.help}\n`;
const obj = t.objectExpression(props);
return t.objectProperty(t.stringLiteral(elt.name), obj);
}).filter((elt) => {
return elt != undefined;
});
return { results, comments };
}
const makeRequire = function(variableName, module, t) {
const decl = t.variableDeclarator(t.identifier(variableName), t.callExpression(t.identifier('require'), [t.stringLiteral(module)]));
return t.variableDeclaration('var', [decl])
}
let docs = ``;
const plugin = function (babel) {
const t = babel.types;
const moduleExports = t.memberExpression(t.identifier('module'), t.identifier('exports'));
return {
visitor: {
ImportDeclaration: function(path) {
path.remove();
},
Program: function(path) {
// Inject the parser's loader
path.unshiftContainer('body', makeRequire('parsers', './parsers', t));
},
ExportDeclaration: function(path) {
// Export declaration on an interface
if (path.node && path.node.declaration && path.node.declaration.type == 'InterfaceDeclaration') {
const { results, comments } = inject(t, doInterface(path.node.declaration));
const id = path.node.declaration.id.name;
const exports = t.memberExpression(moduleExports, t.identifier(id));
docs += `/**\n * @interface ${id}\n${comments} */\n\n`;
path.replaceWith(
t.assignmentExpression('=', exports, t.objectExpression(results))
)
}
}
}
}
};
const auxiliaryCommentBefore = `
**** GENERATED CODE ****
This code has been generated by resources/buildConfigDefinitions.js
Do not edit manually, but update Options/index.js
`
const babel = require("@babel/core");
const res = babel.transformFileSync('./src/Options/index.js', { plugins: [ plugin, '@babel/transform-flow-strip-types' ], babelrc: false, auxiliaryCommentBefore, sourceMaps: false });
require('fs').writeFileSync('./src/Options/Definitions.js', res.code + '\n');
require('fs').writeFileSync('./src/Options/docs.js', docs);
-33
View File
@@ -1,33 +0,0 @@
#!/bin/sh -e
# This script maintains a git branch which mirrors master but in a form that
# what will eventually be deployed to npm, allowing npm dependencies to use:
#
# "parse-server": "parseplatform/parse-server#latest"
#
# From: https://github.com/graphql/graphql-js/blob/master/resources/npm-git.sh
BUILD_DIR=latest
npm run build
mkdir -p $BUILD_DIR
cp package.json $BUILD_DIR/
cp README.md $BUILD_DIR/
cp LICENSE $BUILD_DIR/
cp PATENTS $BUILD_DIR/
cp CHANGELOG.md $BUILD_DIR/
cp -R lib $BUILD_DIR
cp -R bin $BUILD_DIR
cp -R public_html $BUILD_DIR
cp -R views $BUILD_DIR
cd $BUILD_DIR
git init
git config user.name "Travis CI"
git config user.email "github@fb.com"
git add .
git commit -m "Deploy master to LATEST branch"
git push --force --quiet "https://${GH_TOKEN}@github.com/parse-community/parse-server.git" master:latest
+14
View File
@@ -0,0 +1,14 @@
{
"plugins": [
"transform-object-rest-spread"
],
"presets": [
["env", {
"targets": {
"node": "8"
}
}]
],
"sourceMaps": "inline",
"retainLines": true
}
+3 -3
View File
@@ -14,19 +14,19 @@
"Container": true,
"equal": true,
"notEqual": true,
"it_only_db": true,
"it_exclude_dbs": true,
"describe_only_db": true,
"describe_only": true,
"on_db": true,
"defaultConfiguration": true,
"expectSuccess": true,
"range": true,
"expectError": true,
"jequal": true,
"create": true,
"arrayContains": true
},
"rules": {
"no-console": [0]
"no-console": [0],
"no-var": "error"
}
}
+151 -70
View File
@@ -1,8 +1,8 @@
"use strict";
'use strict';
const Config = require("../src/Config");
const Config = require('../lib/Config');
var loginWithWrongCredentialsShouldFail = function(username, password) {
const loginWithWrongCredentialsShouldFail = function(username, password) {
return new Promise((resolve, reject) => {
Parse.User.logIn(username, password)
.then(() => reject('login should have failed'))
@@ -16,13 +16,18 @@ var loginWithWrongCredentialsShouldFail = function(username, password) {
});
};
var isAccountLockoutError = function(username, password, duration, waitTime) {
const isAccountLockoutError = function(username, password, duration, waitTime) {
return new Promise((resolve, reject) => {
setTimeout(() => {
Parse.User.logIn(username, password)
.then(() => reject('login should have failed'))
.catch(err => {
if (err.message === 'Your account is locked due to multiple failed login attempts. Please try again after ' + duration + ' minute(s)') {
if (
err.message ===
'Your account is locked due to multiple failed login attempts. Please try again after ' +
duration +
' minute(s)'
) {
resolve();
} else {
reject(err);
@@ -32,31 +37,41 @@ var isAccountLockoutError = function(username, password, duration, waitTime) {
});
};
describe("Account Lockout Policy: ", () => {
describe('Account Lockout Policy: ', () => {
it('account should not be locked even after failed login attempts if account lockout policy is not set', done => {
reconfigureServer({
appName: 'unlimited',
publicServerURL: 'http://localhost:1337/1',
})
.then(() => {
var user = new Parse.User();
const user = new Parse.User();
user.setUsername('username1');
user.setPassword('password');
return user.signUp(null);
})
.then(() => {
return loginWithWrongCredentialsShouldFail('username1', 'incorrect password 1');
return loginWithWrongCredentialsShouldFail(
'username1',
'incorrect password 1'
);
})
.then(() => {
return loginWithWrongCredentialsShouldFail('username1', 'incorrect password 2');
return loginWithWrongCredentialsShouldFail(
'username1',
'incorrect password 2'
);
})
.then(() => {
return loginWithWrongCredentialsShouldFail('username1', 'incorrect password 3');
return loginWithWrongCredentialsShouldFail(
'username1',
'incorrect password 3'
);
})
.then(() => done())
.catch(err => {
fail('allow unlimited failed login attempts failed: ' + JSON.stringify(err));
fail(
'allow unlimited failed login attempts failed: ' + JSON.stringify(err)
);
done();
});
});
@@ -66,20 +81,27 @@ describe("Account Lockout Policy: ", () => {
appName: 'duration',
accountLockout: {
duration: 'invalid value',
threshold: 5
threshold: 5,
},
publicServerURL: "https://my.public.server.com/1"
publicServerURL: 'https://my.public.server.com/1',
})
.then(() => {
new Config('test');
Config.get('test');
fail('set duration to an invalid number test failed');
done();
})
.catch(err => {
if (err && err === 'Account lockout duration should be greater than 0 and less than 100000') {
if (
err &&
err ===
'Account lockout duration should be greater than 0 and less than 100000'
) {
done();
} else {
fail('set duration to an invalid number test failed: ' + JSON.stringify(err));
fail(
'set duration to an invalid number test failed: ' +
JSON.stringify(err)
);
done();
}
});
@@ -90,20 +112,27 @@ describe("Account Lockout Policy: ", () => {
appName: 'threshold',
accountLockout: {
duration: 5,
threshold: 'invalid number'
threshold: 'invalid number',
},
publicServerURL: "https://my.public.server.com/1"
publicServerURL: 'https://my.public.server.com/1',
})
.then(() => {
new Config('test');
Config.get('test');
fail('set threshold to an invalid number test failed');
done();
})
.catch(err => {
if (err && err === 'Account lockout threshold should be an integer greater than 0 and less than 1000') {
if (
err &&
err ===
'Account lockout threshold should be an integer greater than 0 and less than 1000'
) {
done();
} else {
fail('set threshold to an invalid number test failed: ' + JSON.stringify(err));
fail(
'set threshold to an invalid number test failed: ' +
JSON.stringify(err)
);
done();
}
});
@@ -114,20 +143,26 @@ describe("Account Lockout Policy: ", () => {
appName: 'threshold',
accountLockout: {
duration: 5,
threshold: 0
threshold: 0,
},
publicServerURL: "https://my.public.server.com/1"
publicServerURL: 'https://my.public.server.com/1',
})
.then(() => {
new Config('test');
Config.get('test');
fail('threshold value < 1 is invalid test failed');
done();
})
.catch(err => {
if (err && err === 'Account lockout threshold should be an integer greater than 0 and less than 1000') {
if (
err &&
err ===
'Account lockout threshold should be an integer greater than 0 and less than 1000'
) {
done();
} else {
fail('threshold value < 1 is invalid test failed: ' + JSON.stringify(err));
fail(
'threshold value < 1 is invalid test failed: ' + JSON.stringify(err)
);
done();
}
});
@@ -138,20 +173,27 @@ describe("Account Lockout Policy: ", () => {
appName: 'threshold',
accountLockout: {
duration: 5,
threshold: 1000
threshold: 1000,
},
publicServerURL: "https://my.public.server.com/1"
publicServerURL: 'https://my.public.server.com/1',
})
.then(() => {
new Config('test');
Config.get('test');
fail('threshold value > 999 is invalid test failed');
done();
})
.catch(err => {
if (err && err === 'Account lockout threshold should be an integer greater than 0 and less than 1000') {
if (
err &&
err ===
'Account lockout threshold should be an integer greater than 0 and less than 1000'
) {
done();
} else {
fail('threshold value > 999 is invalid test failed: ' + JSON.stringify(err));
fail(
'threshold value > 999 is invalid test failed: ' +
JSON.stringify(err)
);
done();
}
});
@@ -162,20 +204,26 @@ describe("Account Lockout Policy: ", () => {
appName: 'duration',
accountLockout: {
duration: 0,
threshold: 5
threshold: 5,
},
publicServerURL: "https://my.public.server.com/1"
publicServerURL: 'https://my.public.server.com/1',
})
.then(() => {
new Config('test');
Config.get('test');
fail('duration value < 1 is invalid test failed');
done();
})
.catch(err => {
if (err && err === 'Account lockout duration should be greater than 0 and less than 100000') {
if (
err &&
err ===
'Account lockout duration should be greater than 0 and less than 100000'
) {
done();
} else {
fail('duration value < 1 is invalid test failed: ' + JSON.stringify(err));
fail(
'duration value < 1 is invalid test failed: ' + JSON.stringify(err)
);
done();
}
});
@@ -186,20 +234,27 @@ describe("Account Lockout Policy: ", () => {
appName: 'duration',
accountLockout: {
duration: 100000,
threshold: 5
threshold: 5,
},
publicServerURL: "https://my.public.server.com/1"
publicServerURL: 'https://my.public.server.com/1',
})
.then(() => {
new Config('test');
Config.get('test');
fail('duration value > 99999 is invalid test failed');
done();
})
.catch(err => {
if (err && err === 'Account lockout duration should be greater than 0 and less than 100000') {
if (
err &&
err ===
'Account lockout duration should be greater than 0 and less than 100000'
) {
done();
} else {
fail('duration value > 99999 is invalid test failed: ' + JSON.stringify(err));
fail(
'duration value > 99999 is invalid test failed: ' +
JSON.stringify(err)
);
done();
}
});
@@ -210,21 +265,27 @@ describe("Account Lockout Policy: ", () => {
appName: 'lockout threshold',
accountLockout: {
duration: 1,
threshold: 2
threshold: 2,
},
publicServerURL: "http://localhost:8378/1"
publicServerURL: 'http://localhost:8378/1',
})
.then(() => {
var user = new Parse.User();
user.setUsername("username2");
user.setPassword("failedLoginAttemptsThreshold");
const user = new Parse.User();
user.setUsername('username2');
user.setPassword('failedLoginAttemptsThreshold');
return user.signUp();
})
.then(() => {
return loginWithWrongCredentialsShouldFail('username2', 'wrong password');
return loginWithWrongCredentialsShouldFail(
'username2',
'wrong password'
);
})
.then(() => {
return loginWithWrongCredentialsShouldFail('username2', 'wrong password');
return loginWithWrongCredentialsShouldFail(
'username2',
'wrong password'
);
})
.then(() => {
return isAccountLockoutError('username2', 'wrong password', 1, 1);
@@ -233,7 +294,10 @@ describe("Account Lockout Policy: ", () => {
done();
})
.catch(err => {
fail('lock account after failed login attempts test failed: ' + JSON.stringify(err));
fail(
'lock account after failed login attempts test failed: ' +
JSON.stringify(err)
);
done();
});
});
@@ -243,34 +307,43 @@ describe("Account Lockout Policy: ", () => {
appName: 'lockout threshold',
accountLockout: {
duration: 0.05, // 0.05*60 = 3 secs
threshold: 2
threshold: 2,
},
publicServerURL: "http://localhost:8378/1"
publicServerURL: 'http://localhost:8378/1',
})
.then(() => {
var user = new Parse.User();
user.setUsername("username3");
user.setPassword("failedLoginAttemptsThreshold");
const user = new Parse.User();
user.setUsername('username3');
user.setPassword('failedLoginAttemptsThreshold');
return user.signUp();
})
.then(() => {
return loginWithWrongCredentialsShouldFail('username3', 'wrong password');
return loginWithWrongCredentialsShouldFail(
'username3',
'wrong password'
);
})
.then(() => {
return loginWithWrongCredentialsShouldFail('username3', 'wrong password');
return loginWithWrongCredentialsShouldFail(
'username3',
'wrong password'
);
})
.then(() => {
return isAccountLockoutError('username3', 'wrong password', 0.05, 1);
})
.then(() => {
// account should still be locked even after 2 seconds.
// account should still be locked even after 2 seconds.
return isAccountLockoutError('username3', 'wrong password', 0.05, 2000);
})
.then(() => {
done();
})
.catch(err => {
fail('account should be locked for duration mins test failed: ' + JSON.stringify(err));
fail(
'account should be locked for duration mins test failed: ' +
JSON.stringify(err)
);
done();
});
});
@@ -280,24 +353,30 @@ describe("Account Lockout Policy: ", () => {
appName: 'lockout threshold',
accountLockout: {
duration: 0.05, // 0.05*60 = 3 secs
threshold: 2
threshold: 2,
},
publicServerURL: "http://localhost:8378/1"
publicServerURL: 'http://localhost:8378/1',
})
.then(() => {
var user = new Parse.User();
user.setUsername("username4");
user.setPassword("correct password");
const user = new Parse.User();
user.setUsername('username4');
user.setPassword('correct password');
return user.signUp();
})
.then(() => {
return loginWithWrongCredentialsShouldFail('username4', 'wrong password');
return loginWithWrongCredentialsShouldFail(
'username4',
'wrong password'
);
})
.then(() => {
return loginWithWrongCredentialsShouldFail('username4', 'wrong password');
return loginWithWrongCredentialsShouldFail(
'username4',
'wrong password'
);
})
.then(() => {
// allow locked user to login after 3 seconds with a valid userid and password
// allow locked user to login after 3 seconds with a valid userid and password
return new Promise((resolve, reject) => {
setTimeout(() => {
Parse.User.logIn('username4', 'correct password')
@@ -310,9 +389,11 @@ describe("Account Lockout Policy: ", () => {
done();
})
.catch(err => {
fail('allow login for locked account after accountPolicy.duration minutes test failed: ' + JSON.stringify(err));
fail(
'allow login for locked account after accountPolicy.duration minutes test failed: ' +
JSON.stringify(err)
);
done();
});
});
})
});
+34 -33
View File
@@ -1,83 +1,84 @@
const AdaptableController = require('../lib/Controllers/AdaptableController')
.AdaptableController;
const FilesAdapter = require('../lib/Adapters/Files/FilesAdapter').default;
const FilesController = require('../lib/Controllers/FilesController')
.FilesController;
var AdaptableController = require("../src/Controllers/AdaptableController").AdaptableController;
var FilesAdapter = require("../src/Adapters/Files/FilesAdapter").default;
var FilesController = require("../src/Controllers/FilesController").FilesController;
var MockController = function(options) {
const MockController = function(options) {
AdaptableController.call(this, options);
}
};
MockController.prototype = Object.create(AdaptableController.prototype);
MockController.prototype.constructor = AdaptableController;
describe("AdaptableController", ()=>{
it("should use the provided adapter", (done) => {
var adapter = new FilesAdapter();
var controller = new FilesController(adapter);
describe('AdaptableController', () => {
it('should use the provided adapter', done => {
const adapter = new FilesAdapter();
const controller = new FilesController(adapter);
expect(controller.adapter).toBe(adapter);
// make sure _adapter is private
expect(controller._adapter).toBe(undefined);
// Override _adapter is not doing anything
controller._adapter = "Hello";
controller._adapter = 'Hello';
expect(controller.adapter).toBe(adapter);
done();
});
it("should throw when creating a new mock controller", (done) => {
var adapter = new FilesAdapter();
it('should throw when creating a new mock controller', done => {
const adapter = new FilesAdapter();
expect(() => {
new MockController(adapter);
}).toThrow();
done();
});
it("should fail setting the wrong adapter to the controller", (done) => {
it('should fail setting the wrong adapter to the controller', done => {
function WrongAdapter() {}
var adapter = new FilesAdapter();
var controller = new FilesController(adapter);
var otherAdapter = new WrongAdapter();
const adapter = new FilesAdapter();
const controller = new FilesController(adapter);
const otherAdapter = new WrongAdapter();
expect(() => {
controller.adapter = otherAdapter;
}).toThrow();
done();
});
it("should fail to instantiate a controller with wrong adapter", (done) => {
it('should fail to instantiate a controller with wrong adapter', done => {
function WrongAdapter() {}
var adapter = new WrongAdapter();
const adapter = new WrongAdapter();
expect(() => {
new FilesController(adapter);
}).toThrow();
done();
});
it("should fail to instantiate a controller without an adapter", (done) => {
it('should fail to instantiate a controller without an adapter', done => {
expect(() => {
new FilesController();
}).toThrow();
done();
});
it("should accept an object adapter", (done) => {
var adapter = {
createFile: function() { },
deleteFile: function() { },
getFileData: function() { },
getFileLocation: function() { },
}
it('should accept an object adapter', done => {
const adapter = {
createFile: function() {},
deleteFile: function() {},
getFileData: function() {},
getFileLocation: function() {},
};
expect(() => {
new FilesController(adapter);
}).not.toThrow();
done();
});
it("should accept an object adapter", (done) => {
it('should accept an prototype based object adapter', done => {
function AGoodAdapter() {}
AGoodAdapter.prototype.createFile = function() { };
AGoodAdapter.prototype.deleteFile = function() { };
AGoodAdapter.prototype.getFileData = function() { };
AGoodAdapter.prototype.getFileLocation = function() { };
AGoodAdapter.prototype.createFile = function() {};
AGoodAdapter.prototype.deleteFile = function() {};
AGoodAdapter.prototype.getFileData = function() {};
AGoodAdapter.prototype.getFileLocation = function() {};
var adapter = new AGoodAdapter();
const adapter = new AGoodAdapter();
expect(() => {
new FilesController(adapter);
}).not.toThrow();
+64 -64
View File
@@ -1,41 +1,41 @@
const loadAdapter = require('../lib/Adapters/AdapterLoader').loadAdapter;
const FilesAdapter = require('@parse/fs-files-adapter').default;
const S3Adapter = require('@parse/s3-files-adapter').default;
const ParsePushAdapter = require('@parse/push-adapter').default;
const Config = require('../lib/Config');
var loadAdapter = require("../src/Adapters/AdapterLoader").loadAdapter;
var FilesAdapter = require("parse-server-fs-adapter").default;
var S3Adapter = require("parse-server-s3-adapter").default;
var ParsePushAdapter = require("parse-server-push-adapter").default;
const Config = require('../src/Config');
describe('AdapterLoader', () => {
it('should instantiate an adapter from string in object', done => {
const adapterPath = require('path').resolve('./spec/MockAdapter');
describe("AdapterLoader", ()=>{
it("should instantiate an adapter from string in object", (done) => {
var adapterPath = require('path').resolve("./spec/MockAdapter");
var adapter = loadAdapter({
const adapter = loadAdapter({
adapter: adapterPath,
options: {
key: "value",
foo: "bar"
}
key: 'value',
foo: 'bar',
},
});
expect(adapter instanceof Object).toBe(true);
expect(adapter.options.key).toBe("value");
expect(adapter.options.foo).toBe("bar");
expect(adapter.options.key).toBe('value');
expect(adapter.options.foo).toBe('bar');
done();
});
it("should instantiate an adapter from string", (done) => {
var adapterPath = require('path').resolve("./spec/MockAdapter");
var adapter = loadAdapter(adapterPath);
it('should instantiate an adapter from string', done => {
const adapterPath = require('path').resolve('./spec/MockAdapter');
const adapter = loadAdapter(adapterPath);
expect(adapter instanceof Object).toBe(true);
done();
});
it("should instantiate an adapter from string that is module", (done) => {
var adapterPath = require('path').resolve("./src/Adapters/Files/FilesAdapter");
var adapter = loadAdapter({
adapter: adapterPath
it('should instantiate an adapter from string that is module', done => {
const adapterPath = require('path').resolve(
'./lib/Adapters/Files/FilesAdapter'
);
const adapter = loadAdapter({
adapter: adapterPath,
});
expect(typeof adapter).toBe('object');
@@ -46,9 +46,9 @@ describe("AdapterLoader", ()=>{
done();
});
it("should instantiate an adapter from npm module", (done) => {
var adapter = loadAdapter({
module: 'parse-server-fs-adapter'
it('should instantiate an adapter from npm module', done => {
const adapter = loadAdapter({
module: '@parse/fs-files-adapter',
});
expect(typeof adapter).toBe('object');
@@ -59,83 +59,83 @@ describe("AdapterLoader", ()=>{
done();
});
it("should instantiate an adapter from function/Class", (done) => {
var adapter = loadAdapter({
adapter: FilesAdapter
it('should instantiate an adapter from function/Class', done => {
const adapter = loadAdapter({
adapter: FilesAdapter,
});
expect(adapter instanceof FilesAdapter).toBe(true);
done();
});
it("should instantiate the default adapter from Class", (done) => {
var adapter = loadAdapter(null, FilesAdapter);
it('should instantiate the default adapter from Class', done => {
const adapter = loadAdapter(null, FilesAdapter);
expect(adapter instanceof FilesAdapter).toBe(true);
done();
});
it("should use the default adapter", (done) => {
var defaultAdapter = new FilesAdapter();
var adapter = loadAdapter(null, defaultAdapter);
it('should use the default adapter', done => {
const defaultAdapter = new FilesAdapter();
const adapter = loadAdapter(null, defaultAdapter);
expect(adapter instanceof FilesAdapter).toBe(true);
done();
});
it("should use the provided adapter", (done) => {
var originalAdapter = new FilesAdapter();
var adapter = loadAdapter(originalAdapter);
it('should use the provided adapter', done => {
const originalAdapter = new FilesAdapter();
const adapter = loadAdapter(originalAdapter);
expect(adapter).toBe(originalAdapter);
done();
});
it("should fail loading an improperly configured adapter", (done) => {
var Adapter = function(options) {
it('should fail loading an improperly configured adapter', done => {
const Adapter = function(options) {
if (!options.foo) {
throw "foo is required for that adapter";
throw 'foo is required for that adapter';
}
}
var adapterOptions = {
param: "key",
doSomething: function() {}
};
const adapterOptions = {
param: 'key',
doSomething: function() {},
};
expect(() => {
var adapter = loadAdapter(adapterOptions, Adapter);
const adapter = loadAdapter(adapterOptions, Adapter);
expect(adapter).toEqual(adapterOptions);
}).not.toThrow("foo is required for that adapter");
}).not.toThrow('foo is required for that adapter');
done();
});
it("should load push adapter from options", (done) => {
var options = {
it('should load push adapter from options', done => {
const options = {
android: {
senderId: 'yolo',
apiKey: 'yolo'
}
}
apiKey: 'yolo',
},
};
expect(() => {
var adapter = loadAdapter(undefined, ParsePushAdapter, options);
const adapter = loadAdapter(undefined, ParsePushAdapter, options);
expect(adapter.constructor).toBe(ParsePushAdapter);
expect(adapter).not.toBe(undefined);
}).not.toThrow();
done();
});
it("should load custom push adapter from string (#3544)", (done) => {
var adapterPath = require('path').resolve("./spec/MockPushAdapter");
var options = {
it('should load custom push adapter from string (#3544)', done => {
const adapterPath = require('path').resolve('./spec/MockPushAdapter');
const options = {
ios: {
bundleId: 'bundle.id'
}
}
bundleId: 'bundle.id',
},
};
const pushAdapterOptions = {
adapter: adapterPath,
options
options,
};
expect(() => {
reconfigureServer({
push: pushAdapterOptions,
}).then(() => {
const config = new Config(Parse.applicationId);
const config = Config.get(Parse.applicationId);
const pushAdapter = config.pushWorker.adapter;
expect(pushAdapter.getValidPushTypes()).toEqual(['ios']);
expect(pushAdapter.options).toEqual(pushAdapterOptions);
@@ -144,12 +144,12 @@ describe("AdapterLoader", ()=>{
}).not.toThrow();
});
it("should load S3Adapter from direct passing", (done) => {
var s3Adapter = new S3Adapter("key", "secret", "bucket")
it('should load S3Adapter from direct passing', done => {
const s3Adapter = new S3Adapter('key', 'secret', 'bucket');
expect(() => {
var adapter = loadAdapter(s3Adapter, FilesAdapter);
const adapter = loadAdapter(s3Adapter, FilesAdapter);
expect(adapter).toBe(s3Adapter);
}).not.toThrow();
done();
})
});
});
+82
View File
@@ -0,0 +1,82 @@
const AggregateRouter = require('../lib/Routers/AggregateRouter')
.AggregateRouter;
describe('AggregateRouter', () => {
it('get pipeline from Array', () => {
const body = [
{
group: { objectId: {} },
},
];
const expected = [{ $group: { _id: {} } }];
const result = AggregateRouter.getPipeline(body);
expect(result).toEqual(expected);
});
it('get pipeline from Object', () => {
const body = {
group: { objectId: {} },
};
const expected = [{ $group: { _id: {} } }];
const result = AggregateRouter.getPipeline(body);
expect(result).toEqual(expected);
});
it('get pipeline from Pipeline Operator (Array)', () => {
const body = {
pipeline: [
{
group: { objectId: {} },
},
],
};
const expected = [{ $group: { _id: {} } }];
const result = AggregateRouter.getPipeline(body);
expect(result).toEqual(expected);
});
it('get pipeline from Pipeline Operator (Object)', () => {
const body = {
pipeline: {
group: { objectId: {} },
},
};
const expected = [{ $group: { _id: {} } }];
const result = AggregateRouter.getPipeline(body);
expect(result).toEqual(expected);
});
it('get pipeline fails multiple keys in Array stage ', () => {
const body = [
{
group: { objectId: {} },
match: { name: 'Test' },
},
];
try {
AggregateRouter.getPipeline(body);
} catch (e) {
expect(e.message).toBe(
'Pipeline stages should only have one key found group, match'
);
}
});
it('get pipeline fails multiple keys in Pipeline Operator Array stage ', () => {
const body = {
pipeline: [
{
group: { objectId: {} },
match: { name: 'Test' },
},
],
};
try {
AggregateRouter.getPipeline(body);
} catch (e) {
expect(e.message).toBe(
'Pipeline stages should only have one key found group, match'
);
}
});
});
+57 -49
View File
@@ -1,61 +1,69 @@
const analyticsAdapter = {
appOpened: function() {},
trackEvent: function() {}
}
trackEvent: function() {},
};
describe('AnalyticsController', () => {
it('should track a simple event', (done) => {
it('should track a simple event', done => {
spyOn(analyticsAdapter, 'trackEvent').and.callThrough();
reconfigureServer({
analyticsAdapter
}).then(() => {
return Parse.Analytics.track('MyEvent', {
key: 'value',
count: '0'
})
}).then(() => {
expect(analyticsAdapter.trackEvent).toHaveBeenCalled();
var lastCall = analyticsAdapter.trackEvent.calls.first();
const args = lastCall.args;
expect(args[0]).toEqual('MyEvent');
expect(args[1]).toEqual({
dimensions: {
key: 'value',
count: '0'
}
});
done();
}, (err) => {
fail(JSON.stringify(err));
done();
analyticsAdapter,
})
.then(() => {
return Parse.Analytics.track('MyEvent', {
key: 'value',
count: '0',
});
})
.then(
() => {
expect(analyticsAdapter.trackEvent).toHaveBeenCalled();
const lastCall = analyticsAdapter.trackEvent.calls.first();
const args = lastCall.args;
expect(args[0]).toEqual('MyEvent');
expect(args[1]).toEqual({
dimensions: {
key: 'value',
count: '0',
},
});
done();
},
err => {
fail(JSON.stringify(err));
done();
}
);
});
it('should track a app opened event', (done) => {
it('should track a app opened event', done => {
spyOn(analyticsAdapter, 'appOpened').and.callThrough();
reconfigureServer({
analyticsAdapter
}).then(() => {
return Parse.Analytics.track('AppOpened', {
key: 'value',
count: '0'
})
}).then(() => {
expect(analyticsAdapter.appOpened).toHaveBeenCalled();
var lastCall = analyticsAdapter.appOpened.calls.first();
const args = lastCall.args;
expect(args[0]).toEqual({
dimensions: {
key: 'value',
count: '0'
}
});
done();
}, (err) => {
fail(JSON.stringify(err));
done();
analyticsAdapter,
})
})
})
.then(() => {
return Parse.Analytics.track('AppOpened', {
key: 'value',
count: '0',
});
})
.then(
() => {
expect(analyticsAdapter.appOpened).toHaveBeenCalled();
const lastCall = analyticsAdapter.appOpened.calls.first();
const args = lastCall.args;
expect(args[0]).toEqual({
dimensions: {
key: 'value',
count: '0',
},
});
done();
},
err => {
fail(JSON.stringify(err));
done();
}
);
});
});
+357 -178
View File
@@ -1,157 +1,185 @@
var auth = require('../src/Auth');
var Config = require('../src/Config');
var rest = require('../src/rest');
var AudiencesRouter = require('../src/Routers/AudiencesRouter').AudiencesRouter;
const auth = require('../lib/Auth');
const Config = require('../lib/Config');
const rest = require('../lib/rest');
const request = require('../lib/request');
const AudiencesRouter = require('../lib/Routers/AudiencesRouter')
.AudiencesRouter;
describe('AudiencesRouter', () => {
it('uses find condition from request.body', (done) => {
var config = new Config('test');
var androidAudienceRequest = {
'name': 'Android Users',
'query': '{ "test": "android" }'
it('uses find condition from request.body', done => {
const config = Config.get('test');
const androidAudienceRequest = {
name: 'Android Users',
query: '{ "test": "android" }',
};
var iosAudienceRequest = {
'name': 'Iphone Users',
'query': '{ "test": "ios" }'
const iosAudienceRequest = {
name: 'Iphone Users',
query: '{ "test": "ios" }',
};
var request = {
const request = {
config: config,
auth: auth.master(config),
body: {
where: {
query: '{ "test": "android" }'
}
query: '{ "test": "android" }',
},
},
query: {},
info: {}
info: {},
};
var router = new AudiencesRouter();
rest.create(config, auth.nobody(config), '_Audience', androidAudienceRequest)
const router = new AudiencesRouter();
rest
.create(config, auth.nobody(config), '_Audience', androidAudienceRequest)
.then(() => {
return rest.create(config, auth.nobody(config), '_Audience', iosAudienceRequest);
return rest.create(
config,
auth.nobody(config),
'_Audience',
iosAudienceRequest
);
})
.then(() => {
return router.handleFind(request);
})
.then((res) => {
var results = res.response.results;
.then(res => {
const results = res.response.results;
expect(results.length).toEqual(1);
done();
})
.catch((err) => {
.catch(err => {
fail(JSON.stringify(err));
done();
});
});
it('uses find condition from request.query', (done) => {
var config = new Config('test');
var androidAudienceRequest = {
'name': 'Android Users',
'query': '{ "test": "android" }'
it('uses find condition from request.query', done => {
const config = Config.get('test');
const androidAudienceRequest = {
name: 'Android Users',
query: '{ "test": "android" }',
};
var iosAudienceRequest = {
'name': 'Iphone Users',
'query': '{ "test": "ios" }'
const iosAudienceRequest = {
name: 'Iphone Users',
query: '{ "test": "ios" }',
};
var request = {
const request = {
config: config,
auth: auth.master(config),
body: {},
query: {
where: {
'query': '{ "test": "android" }'
}
query: '{ "test": "android" }',
},
},
info: {}
info: {},
};
var router = new AudiencesRouter();
rest.create(config, auth.nobody(config), '_Audience', androidAudienceRequest)
const router = new AudiencesRouter();
rest
.create(config, auth.nobody(config), '_Audience', androidAudienceRequest)
.then(() => {
return rest.create(config, auth.nobody(config), '_Audience', iosAudienceRequest);
return rest.create(
config,
auth.nobody(config),
'_Audience',
iosAudienceRequest
);
})
.then(() => {
return router.handleFind(request);
})
.then((res) => {
var results = res.response.results;
.then(res => {
const results = res.response.results;
expect(results.length).toEqual(1);
done();
})
.catch((err) => {
.catch(err => {
fail(err);
done();
});
});
it('query installations with limit = 0', (done) => {
var config = new Config('test');
var androidAudienceRequest = {
'name': 'Android Users',
'query': '{ "test": "android" }'
it('query installations with limit = 0', done => {
const config = Config.get('test');
const androidAudienceRequest = {
name: 'Android Users',
query: '{ "test": "android" }',
};
var iosAudienceRequest = {
'name': 'Iphone Users',
'query': '{ "test": "ios" }'
const iosAudienceRequest = {
name: 'Iphone Users',
query: '{ "test": "ios" }',
};
var request = {
const request = {
config: config,
auth: auth.master(config),
body: {},
query: {
limit: 0
limit: 0,
},
info: {}
info: {},
};
new Config('test');
var router = new AudiencesRouter();
rest.create(config, auth.nobody(config), '_Audience', androidAudienceRequest)
Config.get('test');
const router = new AudiencesRouter();
rest
.create(config, auth.nobody(config), '_Audience', androidAudienceRequest)
.then(() => {
return rest.create(config, auth.nobody(config), '_Audience', iosAudienceRequest);
return rest.create(
config,
auth.nobody(config),
'_Audience',
iosAudienceRequest
);
})
.then(() => {
return router.handleFind(request);
})
.then((res) => {
var response = res.response;
.then(res => {
const response = res.response;
expect(response.results.length).toEqual(0);
done();
})
.catch((err) => {
.catch(err => {
fail(JSON.stringify(err));
done();
});
});
it('query installations with count = 1', done => {
var config = new Config('test');
var androidAudienceRequest = {
'name': 'Android Users',
'query': '{ "test": "android" }'
it_exclude_dbs(['postgres'])('query installations with count = 1', done => {
const config = Config.get('test');
const androidAudienceRequest = {
name: 'Android Users',
query: '{ "test": "android" }',
};
var iosAudienceRequest = {
'name': 'Iphone Users',
'query': '{ "test": "ios" }'
const iosAudienceRequest = {
name: 'Iphone Users',
query: '{ "test": "ios" }',
};
var request = {
const request = {
config: config,
auth: auth.master(config),
body: {},
query: {
count: 1
count: 1,
},
info: {}
info: {},
};
var router = new AudiencesRouter();
rest.create(config, auth.nobody(config), '_Audience', androidAudienceRequest)
.then(() => rest.create(config, auth.nobody(config), '_Audience', iosAudienceRequest))
const router = new AudiencesRouter();
rest
.create(config, auth.nobody(config), '_Audience', androidAudienceRequest)
.then(() =>
rest.create(
config,
auth.nobody(config),
'_Audience',
iosAudienceRequest
)
)
.then(() => router.handleFind(request))
.then((res) => {
var response = res.response;
.then(res => {
const response = res.response;
expect(response.results.length).toEqual(2);
expect(response.count).toEqual(2);
done();
@@ -159,66 +187,110 @@ describe('AudiencesRouter', () => {
.catch(error => {
fail(JSON.stringify(error));
done();
})
});
it('query installations with limit = 0 and count = 1', (done) => {
var config = new Config('test');
var androidAudienceRequest = {
'name': 'Android Users',
'query': '{ "test": "android" }'
};
var iosAudienceRequest = {
'name': 'Iphone Users',
'query': '{ "test": "ios" }'
};
var request = {
config: config,
auth: auth.master(config),
body: {},
query: {
limit: 0,
count: 1
},
info: {}
};
var router = new AudiencesRouter();
rest.create(config, auth.nobody(config), '_Audience', androidAudienceRequest)
.then(() => {
return rest.create(config, auth.nobody(config), '_Audience', iosAudienceRequest);
})
.then(() => {
return router.handleFind(request);
})
.then((res) => {
var response = res.response;
expect(response.results.length).toEqual(0);
expect(response.count).toEqual(2);
done();
})
.catch((err) => {
fail(JSON.stringify(err));
done();
});
});
it('should create, read, update and delete audiences throw api', (done) => {
Parse._request('POST', 'push_audiences', { name: 'My Audience', query: JSON.stringify({ deviceType: 'ios' })}, { useMasterKey: true })
.then(() => {
Parse._request('GET', 'push_audiences', {}, { useMasterKey: true }).then((results) => {
it_exclude_dbs(['postgres'])(
'query installations with limit = 0 and count = 1',
done => {
const config = Config.get('test');
const androidAudienceRequest = {
name: 'Android Users',
query: '{ "test": "android" }',
};
const iosAudienceRequest = {
name: 'Iphone Users',
query: '{ "test": "ios" }',
};
const request = {
config: config,
auth: auth.master(config),
body: {},
query: {
limit: 0,
count: 1,
},
info: {},
};
const router = new AudiencesRouter();
rest
.create(
config,
auth.nobody(config),
'_Audience',
androidAudienceRequest
)
.then(() => {
return rest.create(
config,
auth.nobody(config),
'_Audience',
iosAudienceRequest
);
})
.then(() => {
return router.handleFind(request);
})
.then(res => {
const response = res.response;
expect(response.results.length).toEqual(0);
expect(response.count).toEqual(2);
done();
})
.catch(err => {
fail(JSON.stringify(err));
done();
});
}
);
it('should create, read, update and delete audiences throw api', done => {
Parse._request(
'POST',
'push_audiences',
{ name: 'My Audience', query: JSON.stringify({ deviceType: 'ios' }) },
{ useMasterKey: true }
).then(() => {
Parse._request('GET', 'push_audiences', {}, { useMasterKey: true }).then(
results => {
expect(results.results.length).toEqual(1);
expect(results.results[0].name).toEqual('My Audience');
expect(results.results[0].query.deviceType).toEqual('ios');
Parse._request('GET', `push_audiences/${results.results[0].objectId}`, {}, { useMasterKey: true }).then((results) => {
Parse._request(
'GET',
`push_audiences/${results.results[0].objectId}`,
{},
{ useMasterKey: true }
).then(results => {
expect(results.name).toEqual('My Audience');
expect(results.query.deviceType).toEqual('ios');
Parse._request('PUT', `push_audiences/${results.objectId}`, { name: 'My Audience 2' }, { useMasterKey: true }).then(() => {
Parse._request('GET', `push_audiences/${results.objectId}`, {}, { useMasterKey: true }).then((results) => {
Parse._request(
'PUT',
`push_audiences/${results.objectId}`,
{ name: 'My Audience 2' },
{ useMasterKey: true }
).then(() => {
Parse._request(
'GET',
`push_audiences/${results.objectId}`,
{},
{ useMasterKey: true }
).then(results => {
expect(results.name).toEqual('My Audience 2');
expect(results.query.deviceType).toEqual('ios');
Parse._request('DELETE', `push_audiences/${results.objectId}`, {}, { useMasterKey: true }).then(() => {
Parse._request('GET', 'push_audiences', {}, { useMasterKey: true }).then((results) => {
Parse._request(
'DELETE',
`push_audiences/${results.objectId}`,
{},
{ useMasterKey: true }
).then(() => {
Parse._request(
'GET',
'push_audiences',
{},
{ useMasterKey: true }
).then(results => {
expect(results.results.length).toEqual(0);
done();
});
@@ -226,62 +298,169 @@ describe('AudiencesRouter', () => {
});
});
});
});
}
);
});
});
it('should only create with master key', done => {
Parse._request('POST', 'push_audiences', {
name: 'My Audience',
query: JSON.stringify({ deviceType: 'ios' }),
}).then(
() => {},
error => {
expect(error.message).toEqual('unauthorized: master key is required');
done();
}
);
});
it('should only find with master key', done => {
Parse._request('GET', 'push_audiences', {}).then(
() => {},
error => {
expect(error.message).toEqual('unauthorized: master key is required');
done();
}
);
});
it('should only get with master key', done => {
Parse._request('GET', `push_audiences/someId`, {}).then(
() => {},
error => {
expect(error.message).toEqual('unauthorized: master key is required');
done();
}
);
});
it('should only update with master key', done => {
Parse._request('PUT', `push_audiences/someId`, {
name: 'My Audience 2',
}).then(
() => {},
error => {
expect(error.message).toEqual('unauthorized: master key is required');
done();
}
);
});
it('should only delete with master key', done => {
Parse._request('DELETE', `push_audiences/someId`, {}).then(
() => {},
error => {
expect(error.message).toEqual('unauthorized: master key is required');
done();
}
);
});
it_exclude_dbs(['postgres'])(
'should support legacy parse.com audience fields',
done => {
const database = Config.get(Parse.applicationId).database.adapter
.database;
const now = new Date();
Parse._request(
'POST',
'push_audiences',
{ name: 'My Audience', query: JSON.stringify({ deviceType: 'ios' }) },
{ useMasterKey: true }
).then(audience => {
database.collection('test__Audience').updateOne(
{ _id: audience.objectId },
{
$set: {
times_used: 1,
_last_used: now,
},
},
{},
error => {
expect(error).toEqual(null);
database
.collection('test__Audience')
.find({ _id: audience.objectId })
.toArray((error, rows) => {
expect(error).toEqual(null);
expect(rows[0]['times_used']).toEqual(1);
expect(rows[0]['_last_used']).toEqual(now);
Parse._request(
'GET',
'push_audiences/' + audience.objectId,
{},
{ useMasterKey: true }
)
.then(audience => {
expect(audience.name).toEqual('My Audience');
expect(audience.query.deviceType).toEqual('ios');
expect(audience.timesUsed).toEqual(1);
expect(audience.lastUsed).toEqual(now.toISOString());
done();
})
.catch(error => {
done.fail(error);
});
});
}
);
});
}
);
it('should be able to search on audiences', done => {
Parse._request(
'POST',
'push_audiences',
{ name: 'neverUsed', query: JSON.stringify({ deviceType: 'ios' }) },
{ useMasterKey: true }
).then(() => {
const query = {
timesUsed: { $exists: false },
lastUsed: { $exists: false },
};
Parse._request(
'GET',
'push_audiences?order=-createdAt&limit=1',
{ where: query },
{ useMasterKey: true }
)
.then(results => {
expect(results.results.length).toEqual(1);
const audience = results.results[0];
expect(audience.name).toEqual('neverUsed');
done();
})
.catch(error => {
done.fail(error);
});
});
});
it('should only create with master key', (done) => {
Parse._request('POST', 'push_audiences', { name: 'My Audience', query: JSON.stringify({ deviceType: 'ios' })})
.then(
() => {},
(error) => {
expect(error.message).toEqual('unauthorized: master key is required');
done();
}
);
});
it('should only find with master key', (done) => {
Parse._request('GET', 'push_audiences', {})
.then(
() => {},
(error) => {
expect(error.message).toEqual('unauthorized: master key is required');
done();
}
);
});
it('should only get with master key', (done) => {
Parse._request('GET', `push_audiences/someId`, {})
.then(
() => {},
(error) => {
expect(error.message).toEqual('unauthorized: master key is required');
done();
}
);
});
it('should only update with master key', (done) => {
Parse._request('PUT', `push_audiences/someId`, { name: 'My Audience 2' })
.then(
() => {},
(error) => {
expect(error.message).toEqual('unauthorized: master key is required');
done();
}
);
});
it('should only delete with master key', (done) => {
Parse._request('DELETE', `push_audiences/someId`, {})
.then(
() => {},
(error) => {
expect(error.message).toEqual('unauthorized: master key is required');
done();
}
);
it('should handle _Audience invalid fields via rest', async () => {
await reconfigureServer({
appId: 'test',
restAPIKey: 'test',
publicServerURL: 'http://localhost:8378/1',
});
try {
await request({
method: 'POST',
url: 'http://localhost:8378/1/classes/_Audience',
body: { lorem: 'ipsum', _method: 'POST' },
headers: {
'X-Parse-Application-Id': 'test',
'X-Parse-REST-API-Key': 'test',
'Content-Type': 'application/json',
},
});
expect(true).toBeFalsy();
} catch (e) {
expect(e.data.code).toBe(107);
expect(e.data.error).toBe('Could not add field lorem');
}
});
});
+191 -41
View File
@@ -1,11 +1,11 @@
describe('Auth', () => {
var Auth = require('../src/Auth.js').Auth;
const { Auth, getAuthForSessionToken } = require('../lib/Auth.js');
const Config = require('../lib/Config');
describe('getUserRoles', () => {
var auth;
var config;
var currentRoles = null;
var currentUserId = 'userId';
let auth;
let config;
let currentRoles = null;
const currentUserId = 'userId';
beforeEach(() => {
currentRoles = ['role:userId'];
@@ -14,80 +14,230 @@ describe('Auth', () => {
cacheController: {
role: {
get: () => Promise.resolve(currentRoles),
set: jasmine.createSpy('set')
}
}
}
set: jasmine.createSpy('set'),
},
},
};
spyOn(config.cacheController.role, 'get').and.callThrough();
auth = new Auth({
config: config,
isMaster: false,
user: {
id: currentUserId
id: currentUserId,
},
installationId: 'installationId'
installationId: 'installationId',
});
});
it('should get user roles from the cache', (done) => {
auth.getUserRoles()
.then((roles) => {
var firstSet = config.cacheController.role.set.calls.first();
expect(firstSet).toEqual(undefined);
it('should get user roles from the cache', done => {
auth.getUserRoles().then(roles => {
const firstSet = config.cacheController.role.set.calls.first();
expect(firstSet).toEqual(undefined);
var firstGet = config.cacheController.role.get.calls.first();
expect(firstGet.args[0]).toEqual(currentUserId);
expect(roles).toEqual(currentRoles);
done();
});
const firstGet = config.cacheController.role.get.calls.first();
expect(firstGet.args[0]).toEqual(currentUserId);
expect(roles).toEqual(currentRoles);
done();
});
});
it('should only query the roles once', (done) => {
var loadRolesSpy = spyOn(auth, '_loadRoles').and.callThrough();
auth.getUserRoles()
.then((roles) => {
it('should only query the roles once', done => {
const loadRolesSpy = spyOn(auth, '_loadRoles').and.callThrough();
auth
.getUserRoles()
.then(roles => {
expect(roles).toEqual(currentRoles);
return auth.getUserRoles()
return auth.getUserRoles();
})
.then(() => auth.getUserRoles())
.then(() => auth.getUserRoles())
.then((roles) => {
.then(roles => {
// Should only call the cache adapter once.
expect(config.cacheController.role.get.calls.count()).toEqual(1);
expect(loadRolesSpy.calls.count()).toEqual(1);
var firstGet = config.cacheController.role.get.calls.first();
const firstGet = config.cacheController.role.get.calls.first();
expect(firstGet.args[0]).toEqual(currentUserId);
expect(roles).toEqual(currentRoles);
done();
});
});
it('should not have any roles with no user', (done) => {
auth.user = null
auth.getUserRoles()
.then((roles) => expect(roles).toEqual([]))
it('should not have any roles with no user', done => {
auth.user = null;
auth
.getUserRoles()
.then(roles => expect(roles).toEqual([]))
.then(() => done());
});
it('should not have any user roles with master', (done) => {
auth.isMaster = true
auth.getUserRoles()
.then((roles) => expect(roles).toEqual([]))
it('should not have any user roles with master', done => {
auth.isMaster = true;
auth
.getUserRoles()
.then(roles => expect(roles).toEqual([]))
.then(() => done());
});
it('should properly handle bcrypt upgrade', (done) => {
var bcryptOriginal = require('bcrypt-nodejs');
var bcryptNew = require('bcryptjs');
it('should properly handle bcrypt upgrade', done => {
const bcryptOriginal = require('bcrypt-nodejs');
const bcryptNew = require('bcryptjs');
bcryptOriginal.hash('my1Long:password', null, null, function(err, res) {
bcryptNew.compare('my1Long:password', res, function(err, res) {
expect(res).toBeTruthy();
done();
})
});
});
});
});
it('should load auth without a config', async () => {
const user = new Parse.User();
await user.signUp({
username: 'hello',
password: 'password',
});
expect(user.getSessionToken()).not.toBeUndefined();
const userAuth = await getAuthForSessionToken({
sessionToken: user.getSessionToken(),
});
expect(userAuth.user instanceof Parse.User).toBe(true);
expect(userAuth.user.id).toBe(user.id);
});
it('should load auth with a config', async () => {
const user = new Parse.User();
await user.signUp({
username: 'hello',
password: 'password',
});
expect(user.getSessionToken()).not.toBeUndefined();
const userAuth = await getAuthForSessionToken({
sessionToken: user.getSessionToken(),
config: Config.get('test'),
});
expect(userAuth.user instanceof Parse.User).toBe(true);
expect(userAuth.user.id).toBe(user.id);
});
it('should load auth without a config', async () => {
const user = new Parse.User();
await user.signUp({
username: 'hello',
password: 'password',
});
expect(user.getSessionToken()).not.toBeUndefined();
const userAuth = await getAuthForSessionToken({
sessionToken: user.getSessionToken(),
});
expect(userAuth.user instanceof Parse.User).toBe(true);
expect(userAuth.user.id).toBe(user.id);
});
it('should load auth with a config', async () => {
const user = new Parse.User();
await user.signUp({
username: 'hello',
password: 'password',
});
expect(user.getSessionToken()).not.toBeUndefined();
const userAuth = await getAuthForSessionToken({
sessionToken: user.getSessionToken(),
config: Config.get('test'),
});
expect(userAuth.user instanceof Parse.User).toBe(true);
expect(userAuth.user.id).toBe(user.id);
});
describe('getRolesForUser', () => {
const rolesNumber = 300;
it('should load all roles without config', async () => {
const user = new Parse.User();
await user.signUp({
username: 'hello',
password: 'password',
});
expect(user.getSessionToken()).not.toBeUndefined();
const userAuth = await getAuthForSessionToken({
sessionToken: user.getSessionToken(),
});
const roles = [];
for (let i = 0; i < rolesNumber; i++) {
const acl = new Parse.ACL();
const role = new Parse.Role('roleloadtest' + i, acl);
role.getUsers().add([user]);
roles.push(role.save());
}
const savedRoles = await Promise.all(roles);
expect(savedRoles.length).toBe(rolesNumber);
const cloudRoles = await userAuth.getRolesForUser();
expect(cloudRoles.length).toBe(rolesNumber);
});
it('should load all roles with config', async () => {
const user = new Parse.User();
await user.signUp({
username: 'hello',
password: 'password',
});
expect(user.getSessionToken()).not.toBeUndefined();
const userAuth = await getAuthForSessionToken({
sessionToken: user.getSessionToken(),
config: Config.get('test'),
});
const roles = [];
for (let i = 0; i < rolesNumber; i++) {
const acl = new Parse.ACL();
const role = new Parse.Role('roleloadtest' + i, acl);
role.getUsers().add([user]);
roles.push(role.save());
}
const savedRoles = await Promise.all(roles);
expect(savedRoles.length).toBe(rolesNumber);
const cloudRoles = await userAuth.getRolesForUser();
expect(cloudRoles.length).toBe(rolesNumber);
});
it('should load all roles for different users with config', async () => {
const rolesNumber = 100;
const user = new Parse.User();
await user.signUp({
username: 'hello',
password: 'password',
});
const user2 = new Parse.User();
await user2.signUp({
username: 'world',
password: '1234',
});
expect(user.getSessionToken()).not.toBeUndefined();
const userAuth = await getAuthForSessionToken({
sessionToken: user.getSessionToken(),
config: Config.get('test'),
});
const user2Auth = await getAuthForSessionToken({
sessionToken: user2.getSessionToken(),
config: Config.get('test'),
});
const roles = [];
for (let i = 0; i < rolesNumber; i += 1) {
const acl = new Parse.ACL();
const acl2 = new Parse.ACL();
const role = new Parse.Role('roleloadtest' + i, acl);
const role2 = new Parse.Role('role2loadtest' + i, acl2);
role.getUsers().add([user]);
role2.getUsers().add([user2]);
roles.push(role.save());
roles.push(role2.save());
}
const savedRoles = await Promise.all(roles);
expect(savedRoles.length).toBe(rolesNumber * 2);
const cloudRoles = await userAuth.getRolesForUser();
const cloudRoles2 = await user2Auth.getRolesForUser();
expect(cloudRoles.length).toBe(rolesNumber);
expect(cloudRoles2.length).toBe(rolesNumber);
});
});
});
File diff suppressed because it is too large Load Diff
+105 -51
View File
@@ -1,32 +1,33 @@
'use strict';
import commander from '../src/cli/utils/commander';
import definitions from '../src/cli/definitions/parse-server';
import liveQueryDefinitions from '../src/cli/definitions/parse-live-query-server';
const commander = require('../lib/cli/utils/commander').default;
const definitions = require('../lib/cli/definitions/parse-server').default;
const liveQueryDefinitions = require('../lib/cli/definitions/parse-live-query-server')
.default;
var testDefinitions = {
'arg0': 'PROGRAM_ARG_0',
'arg1': {
const testDefinitions = {
arg0: 'PROGRAM_ARG_0',
arg1: {
env: 'PROGRAM_ARG_1',
required: true
required: true,
},
'arg2': {
arg2: {
env: 'PROGRAM_ARG_2',
action: function(value) {
var intValue = parseInt(value);
const intValue = parseInt(value);
if (!Number.isInteger(intValue)) {
throw 'arg2 is invalid';
}
return intValue;
}
},
},
arg3: {},
arg4: {
default: 'arg4Value',
},
'arg3': {},
'arg4': {
default: 'arg4Value'
}
};
describe('commander additions', () => {
afterEach((done) => {
afterEach(done => {
commander.options = [];
delete commander.arg0;
delete commander.arg1;
@@ -36,9 +37,20 @@ describe('commander additions', () => {
done();
});
it('should load properly definitions from args', (done) => {
it('should load properly definitions from args', done => {
commander.loadDefinitions(testDefinitions);
commander.parse(['node','./CLI.spec.js','--arg0', 'arg0Value', '--arg1', 'arg1Value', '--arg2', '2', '--arg3', 'some']);
commander.parse([
'node',
'./CLI.spec.js',
'--arg0',
'arg0Value',
'--arg1',
'arg1Value',
'--arg2',
'2',
'--arg3',
'some',
]);
expect(commander.arg0).toEqual('arg0Value');
expect(commander.arg1).toEqual('arg1Value');
expect(commander.arg2).toEqual(2);
@@ -47,12 +59,12 @@ describe('commander additions', () => {
done();
});
it('should load properly definitions from env', (done) => {
it('should load properly definitions from env', done => {
commander.loadDefinitions(testDefinitions);
commander.parse([], {
'PROGRAM_ARG_0': 'arg0ENVValue',
'PROGRAM_ARG_1': 'arg1ENVValue',
'PROGRAM_ARG_2': '3',
PROGRAM_ARG_0: 'arg0ENVValue',
PROGRAM_ARG_1: 'arg1ENVValue',
PROGRAM_ARG_2: '3',
});
expect(commander.arg0).toEqual('arg0ENVValue');
expect(commander.arg1).toEqual('arg1ENVValue');
@@ -61,14 +73,17 @@ describe('commander additions', () => {
done();
});
it('should load properly use args over env', (done) => {
it('should load properly use args over env', done => {
commander.loadDefinitions(testDefinitions);
commander.parse(['node','./CLI.spec.js','--arg0', 'arg0Value', '--arg4', ''], {
'PROGRAM_ARG_0': 'arg0ENVValue',
'PROGRAM_ARG_1': 'arg1ENVValue',
'PROGRAM_ARG_2': '4',
'PROGRAM_ARG_4': 'arg4ENVValue'
});
commander.parse(
['node', './CLI.spec.js', '--arg0', 'arg0Value', '--arg4', ''],
{
PROGRAM_ARG_0: 'arg0ENVValue',
PROGRAM_ARG_1: 'arg1ENVValue',
PROGRAM_ARG_2: '4',
PROGRAM_ARG_4: 'arg4ENVValue',
}
);
expect(commander.arg0).toEqual('arg0Value');
expect(commander.arg1).toEqual('arg1ENVValue');
expect(commander.arg2).toEqual(4);
@@ -76,24 +91,33 @@ describe('commander additions', () => {
done();
});
it('should fail in action as port is invalid', (done) => {
it('should fail in action as port is invalid', done => {
commander.loadDefinitions(testDefinitions);
expect(()=> {
commander.parse(['node','./CLI.spec.js','--arg0', 'arg0Value'], {
'PROGRAM_ARG_0': 'arg0ENVValue',
'PROGRAM_ARG_1': 'arg1ENVValue',
'PROGRAM_ARG_2': 'hello',
expect(() => {
commander.parse(['node', './CLI.spec.js', '--arg0', 'arg0Value'], {
PROGRAM_ARG_0: 'arg0ENVValue',
PROGRAM_ARG_1: 'arg1ENVValue',
PROGRAM_ARG_2: 'hello',
});
}).toThrow('arg2 is invalid');
done();
});
it('should not override config.json', (done) => {
it('should not override config.json', done => {
commander.loadDefinitions(testDefinitions);
commander.parse(['node','./CLI.spec.js','--arg0', 'arg0Value', './spec/configs/CLIConfig.json'], {
'PROGRAM_ARG_0': 'arg0ENVValue',
'PROGRAM_ARG_1': 'arg1ENVValue',
});
commander.parse(
[
'node',
'./CLI.spec.js',
'--arg0',
'arg0Value',
'./spec/configs/CLIConfig.json',
],
{
PROGRAM_ARG_0: 'arg0ENVValue',
PROGRAM_ARG_1: 'arg1ENVValue',
}
);
const options = commander.getOptions();
expect(options.arg2).toBe(8888);
expect(options.arg3).toBe('hello'); //config value
@@ -101,28 +125,45 @@ describe('commander additions', () => {
done();
});
it('should fail with invalid values in JSON', (done) => {
it('should fail with invalid values in JSON', done => {
commander.loadDefinitions(testDefinitions);
expect(() => {
commander.parse(['node','./CLI.spec.js','--arg0', 'arg0Value', './spec/configs/CLIConfigFail.json'], {
'PROGRAM_ARG_0': 'arg0ENVValue',
'PROGRAM_ARG_1': 'arg1ENVValue',
});
commander.parse(
[
'node',
'./CLI.spec.js',
'--arg0',
'arg0Value',
'./spec/configs/CLIConfigFail.json',
],
{
PROGRAM_ARG_0: 'arg0ENVValue',
PROGRAM_ARG_1: 'arg1ENVValue',
}
);
}).toThrow('arg2 is invalid');
done();
});
it('should fail when too many apps are set', (done) => {
it('should fail when too many apps are set', done => {
commander.loadDefinitions(testDefinitions);
expect(() => {
commander.parse(['node','./CLI.spec.js','./spec/configs/CLIConfigFailTooManyApps.json']);
commander.parse([
'node',
'./CLI.spec.js',
'./spec/configs/CLIConfigFailTooManyApps.json',
]);
}).toThrow('Multiple apps are not supported');
done();
});
it('should load config from apps', (done) => {
it('should load config from apps', done => {
commander.loadDefinitions(testDefinitions);
commander.parse(['node', './CLI.spec.js', './spec/configs/CLIConfigApps.json']);
commander.parse([
'node',
'./CLI.spec.js',
'./spec/configs/CLIConfigApps.json',
]);
const options = commander.getOptions();
expect(options.arg1).toBe('my_app');
expect(options.arg2).toBe(8888);
@@ -131,10 +172,14 @@ describe('commander additions', () => {
done();
});
it('should fail when passing an invalid arguement', (done) => {
it('should fail when passing an invalid arguement', done => {
commander.loadDefinitions(testDefinitions);
expect(() => {
commander.parse(['node', './CLI.spec.js', './spec/configs/CLIConfigUnknownArg.json']);
commander.parse([
'node',
'./CLI.spec.js',
'./spec/configs/CLIConfigUnknownArg.json',
]);
}).toThrow('error: unknown option myArg');
done();
});
@@ -157,6 +202,12 @@ describe('definitions', () => {
}
}
});
it('should throw when using deprecated facebookAppIds', () => {
expect(() => {
definitions.facebookAppIds.action();
}).toThrow();
});
});
describe('LiveQuery definitions', () => {
@@ -167,7 +218,10 @@ describe('LiveQuery definitions', () => {
if (typeof definition.env !== 'undefined') {
expect(typeof definition.env).toBe('string');
}
expect(typeof definition.help).toBe('string');
expect(typeof definition.help).toBe(
'string',
`help for ${key} should be a string`
);
if (typeof definition.required !== 'undefined') {
expect(typeof definition.required).toBe('boolean');
}
+17 -20
View File
@@ -1,24 +1,24 @@
var CacheController = require('../src/Controllers/CacheController.js').default;
const CacheController = require('../lib/Controllers/CacheController.js')
.default;
describe('CacheController', function() {
var FakeCacheAdapter;
var FakeAppID = 'foo';
var KEY = 'hello';
let FakeCacheAdapter;
const FakeAppID = 'foo';
const KEY = 'hello';
beforeEach(() => {
FakeCacheAdapter = {
get: () => Promise.resolve(null),
put: jasmine.createSpy('put'),
del: jasmine.createSpy('del'),
clear: jasmine.createSpy('clear')
}
clear: jasmine.createSpy('clear'),
};
spyOn(FakeCacheAdapter, 'get').and.callThrough();
});
it('should expose role and user caches', (done) => {
var cache = new CacheController(FakeCacheAdapter, FakeAppID);
it('should expose role and user caches', done => {
const cache = new CacheController(FakeCacheAdapter, FakeAppID);
expect(cache.role).not.toEqual(null);
expect(cache.role.get).not.toEqual(null);
@@ -28,27 +28,26 @@ describe('CacheController', function() {
done();
});
['role', 'user'].forEach((cacheName) => {
['role', 'user'].forEach(cacheName => {
it('should prefix ' + cacheName + ' cache', () => {
var cache = new CacheController(FakeCacheAdapter, FakeAppID)[cacheName];
const cache = new CacheController(FakeCacheAdapter, FakeAppID)[cacheName];
cache.put(KEY, 'world');
var firstPut = FakeCacheAdapter.put.calls.first();
const firstPut = FakeCacheAdapter.put.calls.first();
expect(firstPut.args[0]).toEqual([FakeAppID, cacheName, KEY].join(':'));
cache.get(KEY);
var firstGet = FakeCacheAdapter.get.calls.first();
const firstGet = FakeCacheAdapter.get.calls.first();
expect(firstGet.args[0]).toEqual([FakeAppID, cacheName, KEY].join(':'));
cache.del(KEY);
var firstDel = FakeCacheAdapter.del.calls.first();
const firstDel = FakeCacheAdapter.del.calls.first();
expect(firstDel.args[0]).toEqual([FakeAppID, cacheName, KEY].join(':'));
});
});
it('should clear the entire cache', () => {
var cache = new CacheController(FakeCacheAdapter, FakeAppID);
const cache = new CacheController(FakeCacheAdapter, FakeAppID);
cache.clear();
expect(FakeCacheAdapter.clear.calls.count()).toEqual(1);
@@ -60,15 +59,13 @@ describe('CacheController', function() {
expect(FakeCacheAdapter.clear.calls.count()).toEqual(3);
});
it('should handle cache rejections', (done) => {
it('should handle cache rejections', done => {
FakeCacheAdapter.get = () => Promise.reject();
var cache = new CacheController(FakeCacheAdapter, FakeAppID);
const cache = new CacheController(FakeCacheAdapter, FakeAppID);
cache.get('foo').then(done, () => {
fail('Promise should not be rejected.');
});
});
});
+101 -95
View File
@@ -1,10 +1,11 @@
var Client = require('../src/LiveQuery/Client').Client;
var ParseWebSocket = require('../src/LiveQuery/ParseWebSocketServer').ParseWebSocket;
const Client = require('../lib/LiveQuery/Client').Client;
const ParseWebSocket = require('../lib/LiveQuery/ParseWebSocketServer')
.ParseWebSocket;
describe('Client', function() {
it('can be initialized', function() {
var parseWebSocket = new ParseWebSocket({});
var client = new Client(1, parseWebSocket);
const parseWebSocket = new ParseWebSocket({});
const client = new Client(1, parseWebSocket);
expect(client.id).toBe(1);
expect(client.parseWebSocket).toBe(parseWebSocket);
@@ -12,8 +13,8 @@ describe('Client', function() {
});
it('can push response', function() {
var parseWebSocket = {
send: jasmine.createSpy('send')
const parseWebSocket = {
send: jasmine.createSpy('send'),
};
Client.pushResponse(parseWebSocket, 'message');
@@ -21,13 +22,13 @@ describe('Client', function() {
});
it('can push error', function() {
var parseWebSocket = {
send: jasmine.createSpy('send')
const parseWebSocket = {
send: jasmine.createSpy('send'),
};
Client.pushError(parseWebSocket, 1, 'error', true);
var lastCall = parseWebSocket.send.calls.first();
var messageJSON = JSON.parse(lastCall.args[0]);
const lastCall = parseWebSocket.send.calls.first();
const messageJSON = JSON.parse(lastCall.args[0]);
expect(messageJSON.op).toBe('error');
expect(messageJSON.error).toBe('error');
expect(messageJSON.code).toBe(1);
@@ -35,13 +36,13 @@ describe('Client', function() {
});
it('can add subscription information', function() {
var subscription = {};
var fields = ['test'];
var subscriptionInfo = {
const subscription = {};
const fields = ['test'];
const subscriptionInfo = {
subscription: subscription,
fields: fields
}
var client = new Client(1, {});
fields: fields,
};
const client = new Client(1, {});
client.addSubscriptionInfo(1, subscriptionInfo);
expect(client.subscriptionInfos.size).toBe(1);
@@ -49,73 +50,76 @@ describe('Client', function() {
});
it('can get subscription information', function() {
var subscription = {};
var fields = ['test'];
var subscriptionInfo = {
const subscription = {};
const fields = ['test'];
const subscriptionInfo = {
subscription: subscription,
fields: fields
}
var client = new Client(1, {});
fields: fields,
};
const client = new Client(1, {});
client.addSubscriptionInfo(1, subscriptionInfo);
var subscriptionInfoAgain = client.getSubscriptionInfo(1);
const subscriptionInfoAgain = client.getSubscriptionInfo(1);
expect(subscriptionInfoAgain).toBe(subscriptionInfo);
});
it('can delete subscription information', function() {
var subscription = {};
var fields = ['test'];
var subscriptionInfo = {
const subscription = {};
const fields = ['test'];
const subscriptionInfo = {
subscription: subscription,
fields: fields
}
var client = new Client(1, {});
fields: fields,
};
const client = new Client(1, {});
client.addSubscriptionInfo(1, subscriptionInfo);
client.deleteSubscriptionInfo(1);
expect(client.subscriptionInfos.size).toBe(0);
});
it('can generate ParseObject JSON with null selected field', function() {
var parseObjectJSON = {
key : 'value',
const parseObjectJSON = {
key: 'value',
className: 'test',
objectId: 'test',
updatedAt: '2015-12-07T21:27:13.746Z',
createdAt: '2015-12-07T21:27:13.746Z',
ACL: 'test',
};
var client = new Client(1, {});
const client = new Client(1, {});
expect(client._toJSONWithFields(parseObjectJSON, null)).toBe(parseObjectJSON);
expect(client._toJSONWithFields(parseObjectJSON, null)).toBe(
parseObjectJSON
);
});
it('can generate ParseObject JSON with undefined selected field', function() {
var parseObjectJSON = {
key : 'value',
const parseObjectJSON = {
key: 'value',
className: 'test',
objectId: 'test',
updatedAt: '2015-12-07T21:27:13.746Z',
createdAt: '2015-12-07T21:27:13.746Z',
ACL: 'test',
};
var client = new Client(1, {});
const client = new Client(1, {});
expect(client._toJSONWithFields(parseObjectJSON, undefined)).toBe(parseObjectJSON);
expect(client._toJSONWithFields(parseObjectJSON, undefined)).toBe(
parseObjectJSON
);
});
it('can generate ParseObject JSON with selected fields', function() {
var parseObjectJSON = {
key : 'value',
const parseObjectJSON = {
key: 'value',
className: 'test',
objectId: 'test',
updatedAt: '2015-12-07T21:27:13.746Z',
createdAt: '2015-12-07T21:27:13.746Z',
ACL: 'test',
test: 'test'
test: 'test',
};
var client = new Client(1, {});
const client = new Client(1, {});
expect(client._toJSONWithFields(parseObjectJSON, ['test'])).toEqual({
className: 'test',
@@ -123,22 +127,24 @@ describe('Client', function() {
updatedAt: '2015-12-07T21:27:13.746Z',
createdAt: '2015-12-07T21:27:13.746Z',
ACL: 'test',
test: 'test'
test: 'test',
});
});
it('can generate ParseObject JSON with nonexistent selected fields', function() {
var parseObjectJSON = {
key : 'value',
const parseObjectJSON = {
key: 'value',
className: 'test',
objectId: 'test',
updatedAt: '2015-12-07T21:27:13.746Z',
createdAt: '2015-12-07T21:27:13.746Z',
ACL: 'test',
test: 'test'
test: 'test',
};
var client = new Client(1, {});
var limitedParseObject = client._toJSONWithFields(parseObjectJSON, ['name']);
const client = new Client(1, {});
const limitedParseObject = client._toJSONWithFields(parseObjectJSON, [
'name',
]);
expect(limitedParseObject).toEqual({
className: 'test',
@@ -151,64 +157,64 @@ describe('Client', function() {
});
it('can push connect response', function() {
var parseWebSocket = {
send: jasmine.createSpy('send')
const parseWebSocket = {
send: jasmine.createSpy('send'),
};
var client = new Client(1, parseWebSocket);
const client = new Client(1, parseWebSocket);
client.pushConnect();
var lastCall = parseWebSocket.send.calls.first();
var messageJSON = JSON.parse(lastCall.args[0]);
const lastCall = parseWebSocket.send.calls.first();
const messageJSON = JSON.parse(lastCall.args[0]);
expect(messageJSON.op).toBe('connected');
expect(messageJSON.clientId).toBe(1);
});
it('can push subscribe response', function() {
var parseWebSocket = {
send: jasmine.createSpy('send')
const parseWebSocket = {
send: jasmine.createSpy('send'),
};
var client = new Client(1, parseWebSocket);
const client = new Client(1, parseWebSocket);
client.pushSubscribe(2);
var lastCall = parseWebSocket.send.calls.first();
var messageJSON = JSON.parse(lastCall.args[0]);
const lastCall = parseWebSocket.send.calls.first();
const messageJSON = JSON.parse(lastCall.args[0]);
expect(messageJSON.op).toBe('subscribed');
expect(messageJSON.clientId).toBe(1);
expect(messageJSON.requestId).toBe(2);
});
it('can push unsubscribe response', function() {
var parseWebSocket = {
send: jasmine.createSpy('send')
const parseWebSocket = {
send: jasmine.createSpy('send'),
};
var client = new Client(1, parseWebSocket);
const client = new Client(1, parseWebSocket);
client.pushUnsubscribe(2);
var lastCall = parseWebSocket.send.calls.first();
var messageJSON = JSON.parse(lastCall.args[0]);
const lastCall = parseWebSocket.send.calls.first();
const messageJSON = JSON.parse(lastCall.args[0]);
expect(messageJSON.op).toBe('unsubscribed');
expect(messageJSON.clientId).toBe(1);
expect(messageJSON.requestId).toBe(2);
});
it('can push create response', function() {
var parseObjectJSON = {
key : 'value',
const parseObjectJSON = {
key: 'value',
className: 'test',
objectId: 'test',
updatedAt: '2015-12-07T21:27:13.746Z',
createdAt: '2015-12-07T21:27:13.746Z',
ACL: 'test',
test: 'test'
test: 'test',
};
var parseWebSocket = {
send: jasmine.createSpy('send')
const parseWebSocket = {
send: jasmine.createSpy('send'),
};
var client = new Client(1, parseWebSocket);
const client = new Client(1, parseWebSocket);
client.pushCreate(2, parseObjectJSON);
var lastCall = parseWebSocket.send.calls.first();
var messageJSON = JSON.parse(lastCall.args[0]);
const lastCall = parseWebSocket.send.calls.first();
const messageJSON = JSON.parse(lastCall.args[0]);
expect(messageJSON.op).toBe('create');
expect(messageJSON.clientId).toBe(1);
expect(messageJSON.requestId).toBe(2);
@@ -216,23 +222,23 @@ describe('Client', function() {
});
it('can push enter response', function() {
var parseObjectJSON = {
key : 'value',
const parseObjectJSON = {
key: 'value',
className: 'test',
objectId: 'test',
updatedAt: '2015-12-07T21:27:13.746Z',
createdAt: '2015-12-07T21:27:13.746Z',
ACL: 'test',
test: 'test'
test: 'test',
};
var parseWebSocket = {
send: jasmine.createSpy('send')
const parseWebSocket = {
send: jasmine.createSpy('send'),
};
var client = new Client(1, parseWebSocket);
const client = new Client(1, parseWebSocket);
client.pushEnter(2, parseObjectJSON);
var lastCall = parseWebSocket.send.calls.first();
var messageJSON = JSON.parse(lastCall.args[0]);
const lastCall = parseWebSocket.send.calls.first();
const messageJSON = JSON.parse(lastCall.args[0]);
expect(messageJSON.op).toBe('enter');
expect(messageJSON.clientId).toBe(1);
expect(messageJSON.requestId).toBe(2);
@@ -240,23 +246,23 @@ describe('Client', function() {
});
it('can push update response', function() {
var parseObjectJSON = {
key : 'value',
const parseObjectJSON = {
key: 'value',
className: 'test',
objectId: 'test',
updatedAt: '2015-12-07T21:27:13.746Z',
createdAt: '2015-12-07T21:27:13.746Z',
ACL: 'test',
test: 'test'
test: 'test',
};
var parseWebSocket = {
send: jasmine.createSpy('send')
const parseWebSocket = {
send: jasmine.createSpy('send'),
};
var client = new Client(1, parseWebSocket);
const client = new Client(1, parseWebSocket);
client.pushUpdate(2, parseObjectJSON);
var lastCall = parseWebSocket.send.calls.first();
var messageJSON = JSON.parse(lastCall.args[0]);
const lastCall = parseWebSocket.send.calls.first();
const messageJSON = JSON.parse(lastCall.args[0]);
expect(messageJSON.op).toBe('update');
expect(messageJSON.clientId).toBe(1);
expect(messageJSON.requestId).toBe(2);
@@ -264,23 +270,23 @@ describe('Client', function() {
});
it('can push leave response', function() {
var parseObjectJSON = {
key : 'value',
const parseObjectJSON = {
key: 'value',
className: 'test',
objectId: 'test',
updatedAt: '2015-12-07T21:27:13.746Z',
createdAt: '2015-12-07T21:27:13.746Z',
ACL: 'test',
test: 'test'
test: 'test',
};
var parseWebSocket = {
send: jasmine.createSpy('send')
const parseWebSocket = {
send: jasmine.createSpy('send'),
};
var client = new Client(1, parseWebSocket);
const client = new Client(1, parseWebSocket);
client.pushLeave(2, parseObjectJSON);
var lastCall = parseWebSocket.send.calls.first();
var messageJSON = JSON.parse(lastCall.args[0]);
const lastCall = parseWebSocket.send.calls.first();
const messageJSON = JSON.parse(lastCall.args[0]);
expect(messageJSON.op).toBe('leave');
expect(messageJSON.clientId).toBe(1);
expect(messageJSON.requestId).toBe(2);
+27 -19
View File
@@ -1,41 +1,49 @@
var ClientSDK = require('../src/ClientSDK');
const ClientSDK = require('../lib/ClientSDK');
describe('ClientSDK', () => {
it('should properly parse the SDK versions', () => {
const clientSDKFromVersion = ClientSDK.fromString;
expect(clientSDKFromVersion('i1.1.1')).toEqual({
sdk: 'i',
version: '1.1.1'
version: '1.1.1',
});
expect(clientSDKFromVersion('i1')).toEqual({
sdk: 'i',
version: '1'
version: '1',
});
expect(clientSDKFromVersion('apple-tv1.13.0')).toEqual({
sdk: 'apple-tv',
version: '1.13.0'
version: '1.13.0',
});
expect(clientSDKFromVersion('js1.9.0')).toEqual({
sdk: 'js',
version: '1.9.0'
version: '1.9.0',
});
});
it('should properly sastisfy', () => {
expect(ClientSDK.compatible({
js: '>=1.9.0'
})("js1.9.0")).toBe(true);
expect(
ClientSDK.compatible({
js: '>=1.9.0',
})('js1.9.0')
).toBe(true);
expect(ClientSDK.compatible({
js: '>=1.9.0'
})("js2.0.0")).toBe(true);
expect(
ClientSDK.compatible({
js: '>=1.9.0',
})('js2.0.0')
).toBe(true);
expect(ClientSDK.compatible({
js: '>=1.9.0'
})("js1.8.0")).toBe(false);
expect(
ClientSDK.compatible({
js: '>=1.9.0',
})('js1.8.0')
).toBe(false);
expect(ClientSDK.compatible({
js: '>=1.9.0'
})(undefined)).toBe(true);
})
})
expect(
ClientSDK.compatible({
js: '>=1.9.0',
})(undefined)
).toBe(true);
});
});
+1689 -793
View File
File diff suppressed because it is too large Load Diff
+157 -127
View File
@@ -1,105 +1,119 @@
const LoggerController = require('../src/Controllers/LoggerController').LoggerController;
const WinstonLoggerAdapter = require('../src/Adapters/Logger/WinstonLoggerAdapter').WinstonLoggerAdapter;
const LoggerController = require('../lib/Controllers/LoggerController')
.LoggerController;
const WinstonLoggerAdapter = require('../lib/Adapters/Logger/WinstonLoggerAdapter')
.WinstonLoggerAdapter;
const fs = require('fs');
const Config = require('../lib/Config');
const loremFile = __dirname + '/support/lorem.txt';
describe("Cloud Code Logger", () => {
describe('Cloud Code Logger', () => {
let user;
beforeEach(done => {
let spy;
beforeEach(async () => {
Parse.User.enableUnsafeCurrentUser();
return reconfigureServer({
// useful to flip to false for fine tuning :).
silent: true,
}).then(() => {
return Parse.User.signUp('tester', 'abc')
.then(loggedInUser => user = loggedInUser)
.then(() => Parse.User.logIn(user.get('username'), 'abc'))
.then(() => done())
});
})
.then(() => {
return Parse.User.signUp('tester', 'abc')
.catch(() => {})
.then(loggedInUser => (user = loggedInUser))
.then(() => Parse.User.logIn(user.get('username'), 'abc'));
})
.then(() => {
spy = spyOn(
Config.get('test').loggerController.adapter,
'log'
).and.callThrough();
});
});
// Note that helpers takes care of logout.
// see helpers.js:afterEach
it("should expose log to functions", done => {
var logController = new LoggerController(new WinstonLoggerAdapter());
Parse.Cloud.define("loggerTest", (req, res) => {
it('should expose log to functions', () => {
const spy = spyOn(
Config.get('test').loggerController,
'log'
).and.callThrough();
Parse.Cloud.define('loggerTest', req => {
req.log.info('logTest', 'info log', { info: 'some log' });
req.log.error('logTest', 'error log', { error: 'there was an error' });
res.success({});
return {};
});
Parse.Cloud.run('loggerTest').then(() => {
return logController.getLogs({ from: Date.now() - 500, size: 1000 });
}).then((res) => {
expect(res.length).not.toBe(0);
const lastLogs = res.slice(0, 3);
const cloudFunctionMessage = lastLogs[0];
const errorMessage = lastLogs[1];
const infoMessage = lastLogs[2];
expect(cloudFunctionMessage.level).toBe('info');
expect(cloudFunctionMessage.params).toEqual({});
expect(cloudFunctionMessage.message).toMatch(/Ran cloud function loggerTest for user [^ ]* with:\n {2}Input: {}\n {2}Result: {}/);
expect(cloudFunctionMessage.functionName).toEqual('loggerTest');
expect(errorMessage.level).toBe('error');
expect(errorMessage.error).toBe('there was an error');
expect(errorMessage.message).toBe('logTest error log');
expect(infoMessage.level).toBe('info');
expect(infoMessage.info).toBe('some log');
expect(infoMessage.message).toBe('logTest info log');
done();
return Parse.Cloud.run('loggerTest').then(() => {
expect(spy).toHaveBeenCalledTimes(3);
const cloudFunctionMessage = spy.calls.all()[2];
const errorMessage = spy.calls.all()[1];
const infoMessage = spy.calls.all()[0];
expect(cloudFunctionMessage.args[0]).toBe('info');
expect(cloudFunctionMessage.args[1][1].params).toEqual({});
expect(cloudFunctionMessage.args[1][0]).toMatch(
/Ran cloud function loggerTest for user [^ ]* with:\n {2}Input: {}\n {2}Result: {}/
);
expect(cloudFunctionMessage.args[1][1].functionName).toEqual(
'loggerTest'
);
expect(errorMessage.args[0]).toBe('error');
expect(errorMessage.args[1][2].error).toBe('there was an error');
expect(errorMessage.args[1][0]).toBe('logTest');
expect(errorMessage.args[1][1]).toBe('error log');
expect(infoMessage.args[0]).toBe('info');
expect(infoMessage.args[1][2].info).toBe('some log');
expect(infoMessage.args[1][0]).toBe('logTest');
expect(infoMessage.args[1][1]).toBe('info log');
});
});
it('trigger should obfuscate password', done => {
const logController = new LoggerController(new WinstonLoggerAdapter());
Parse.Cloud.beforeSave(Parse.User, (req, res) => {
res.success(req.object);
Parse.Cloud.beforeSave(Parse.User, req => {
return req.object;
});
Parse.User.signUp('tester123', 'abc')
.then(() => logController.getLogs({ from: Date.now() - 500, size: 1000 }))
.then((res) => {
const entry = res[0];
expect(entry.message).not.toMatch(/password":"abc/);
expect(entry.message).toMatch(/\*\*\*\*\*\*\*\*/);
.then(() => {
const entry = spy.calls.mostRecent().args;
expect(entry[1]).not.toMatch(/password":"abc/);
expect(entry[1]).toMatch(/\*\*\*\*\*\*\*\*/);
done();
})
.then(null, e => done.fail(e));
});
it("should expose log to trigger", (done) => {
var logController = new LoggerController(new WinstonLoggerAdapter());
Parse.Cloud.beforeSave("MyObject", (req, res) => {
it('should expose log to trigger', done => {
Parse.Cloud.beforeSave('MyObject', req => {
req.log.info('beforeSave MyObject', 'info log', { info: 'some log' });
req.log.error('beforeSave MyObject', 'error log', { error: 'there was an error' });
res.success({});
req.log.error('beforeSave MyObject', 'error log', {
error: 'there was an error',
});
return {};
});
const obj = new Parse.Object('MyObject');
obj.save().then(() => {
return logController.getLogs({ from: Date.now() - 500, size: 1000 })
}).then((res) => {
expect(res.length).not.toBe(0);
const lastLogs = res.slice(0, 3);
const cloudTriggerMessage = lastLogs[0];
const errorMessage = lastLogs[1];
const infoMessage = lastLogs[2];
expect(cloudTriggerMessage.level).toBe('info');
expect(cloudTriggerMessage.triggerType).toEqual('beforeSave');
expect(cloudTriggerMessage.message).toMatch(/beforeSave triggered for MyObject for user [^ ]*\n {2}Input: {}\n {2}Result: {}/);
expect(cloudTriggerMessage.user).toBe(user.id);
expect(errorMessage.level).toBe('error');
expect(errorMessage.error).toBe('there was an error');
expect(errorMessage.message).toBe('beforeSave MyObject error log');
expect(infoMessage.level).toBe('info');
expect(infoMessage.info).toBe('some log');
expect(infoMessage.message).toBe('beforeSave MyObject info log');
const lastCalls = spy.calls.all().reverse();
const cloudTriggerMessage = lastCalls[0].args;
const errorMessage = lastCalls[1].args;
const infoMessage = lastCalls[2].args;
expect(cloudTriggerMessage[0]).toBe('info');
expect(cloudTriggerMessage[2].triggerType).toEqual('beforeSave');
expect(cloudTriggerMessage[1]).toMatch(
/beforeSave triggered for MyObject for user [^ ]*\n {2}Input: {}\n {2}Result: {"object":{}}/
);
expect(cloudTriggerMessage[2].user).toBe(user.id);
expect(errorMessage[0]).toBe('error');
expect(errorMessage[3].error).toBe('there was an error');
expect(errorMessage[1] + ' ' + errorMessage[2]).toBe(
'beforeSave MyObject error log'
);
expect(infoMessage[0]).toBe('info');
expect(infoMessage[3].info).toBe('some log');
expect(infoMessage[1] + ' ' + infoMessage[2]).toBe(
'beforeSave MyObject info log'
);
done();
});
});
@@ -112,43 +126,39 @@ describe("Cloud Code Logger", () => {
});
it('should truncate input and result of long lines', done => {
const logController = new LoggerController(new WinstonLoggerAdapter());
const longString = fs.readFileSync(loremFile, 'utf8');
Parse.Cloud.define('aFunction', (req, res) => {
res.success(req.params);
Parse.Cloud.define('aFunction', req => {
return req.params;
});
Parse.Cloud.run('aFunction', { longString })
.then(() => logController.getLogs({ from: Date.now() - 500, size: 1000 }))
.then(logs => {
const log = logs[0];
expect(log.level).toEqual('info');
expect(log.message).toMatch(
/Ran cloud function aFunction for user [^ ]* with:\n {2}Input: {.*?\(truncated\)$/m);
.then(() => {
const log = spy.calls.mostRecent().args;
expect(log[0]).toEqual('info');
expect(log[1]).toMatch(
/Ran cloud function aFunction for user [^ ]* with:\n {2}Input: {.*?\(truncated\)$/m
);
done();
})
.then(null, e => done.fail(e));
});
it('should log an afterSave', done => {
const logController = new LoggerController(new WinstonLoggerAdapter());
Parse.Cloud.afterSave("MyObject", () => { });
Parse.Cloud.afterSave('MyObject', () => {});
new Parse.Object('MyObject')
.save()
.then(() => logController.getLogs({ from: Date.now() - 500, size: 1000 }))
.then((logs) => {
const log = logs[0];
expect(log.triggerType).toEqual('afterSave');
.then(() => {
const log = spy.calls.mostRecent().args;
expect(log[2].triggerType).toEqual('afterSave');
done();
})
// catch errors - not that the error is actually useful :(
// catch errors - not that the error is actually useful :(
.then(null, e => done.fail(e));
});
it('should log a denied beforeSave', done => {
const logController = new LoggerController(new WinstonLoggerAdapter());
Parse.Cloud.beforeSave("MyObject", (req, res) => {
res.error('uh oh!');
Parse.Cloud.beforeSave('MyObject', () => {
throw 'uh oh!';
});
new Parse.Object('MyObject')
@@ -157,58 +167,64 @@ describe("Cloud Code Logger", () => {
() => done.fail('this is not supposed to succeed'),
() => new Promise(resolve => setTimeout(resolve, 100))
)
.then(() => logController.getLogs({ from: Date.now() - 500, size: 1000 }))
.then(logs => {
const log = logs[1]; // 0 is the 'uh oh!' from rejection...
expect(log.level).toEqual('error');
expect(log.error).toEqual({ code: 141, message: 'uh oh!' });
done()
.then(() => {
const logs = spy.calls.all().reverse();
const log = logs[1].args; // 0 is the 'uh oh!' from rejection...
expect(log[0]).toEqual('error');
const error = log[2].error;
expect(error instanceof Parse.Error).toBeTruthy();
expect(error.code).toBe(141);
expect(error.message).toBe('uh oh!');
done();
});
});
it('should log cloud function success', done => {
const logController = new LoggerController(new WinstonLoggerAdapter());
Parse.Cloud.define('aFunction', (req, res) => {
res.success('it worked!');
Parse.Cloud.define('aFunction', () => {
return 'it worked!';
});
Parse.Cloud.run('aFunction', { foo: 'bar' })
.then(() => logController.getLogs({ from: Date.now() - 500, size: 1000 }))
.then(logs => {
const log = logs[0];
expect(log.level).toEqual('info');
expect(log.message).toMatch(
/Ran cloud function aFunction for user [^ ]* with:\n {2}Input: {"foo":"bar"}\n {2}Result: "it worked!/);
done();
});
Parse.Cloud.run('aFunction', { foo: 'bar' }).then(() => {
const log = spy.calls.mostRecent().args;
expect(log[0]).toEqual('info');
expect(log[1]).toMatch(
/Ran cloud function aFunction for user [^ ]* with:\n {2}Input: {"foo":"bar"}\n {2}Result: "it worked!/
);
done();
});
});
it('should log cloud function failure', done => {
const logController = new LoggerController(new WinstonLoggerAdapter());
Parse.Cloud.define('aFunction', (req, res) => {
res.error('it failed!');
Parse.Cloud.define('aFunction', () => {
throw 'it failed!';
});
Parse.Cloud.run('aFunction', { foo: 'bar' })
.then(null, () => logController.getLogs({ from: Date.now() - 500, size: 1000 }))
.then(logs => {
const log = logs[2];
expect(log.level).toEqual('error');
expect(log.message).toMatch(
/Failed running cloud function aFunction for user [^ ]* with:\n {2}Input: {"foo":"bar"}\n {2}Error: {"code":141,"message":"it failed!"}/);
.catch(() => {})
.then(() => {
const logs = spy.calls.all().reverse();
expect(logs[0].args[1]).toBe('Parse error: ');
expect(logs[0].args[2].message).toBe('it failed!');
const log = logs[1].args;
expect(log[0]).toEqual('error');
expect(log[1]).toMatch(
/Failed running cloud function aFunction for user [^ ]* with:\n {2}Input: {"foo":"bar"}\n {2}Error:/
);
const errorString = JSON.stringify(new Parse.Error(141, 'it failed!'));
expect(log[1].indexOf(errorString)).toBeGreaterThan(0);
done();
});
})
.catch(done.fail);
});
xit('should log a changed beforeSave indicating a change', done => {
const logController = new LoggerController(new WinstonLoggerAdapter());
Parse.Cloud.beforeSave("MyObject", (req, res) => {
Parse.Cloud.beforeSave('MyObject', req => {
const myObj = req.object;
myObj.set('aChange', true);
res.success(myObj);
return myObj;
});
new Parse.Object('MyObject')
@@ -228,19 +244,33 @@ describe("Cloud Code Logger", () => {
}).pend('needs more work.....');
it('cloud function should obfuscate password', done => {
const logController = new LoggerController(new WinstonLoggerAdapter());
Parse.Cloud.define('testFunction', (req, res) => {
res.success(1002,'verify code success');
Parse.Cloud.define('testFunction', () => {
return 'verify code success';
});
Parse.Cloud.run('testFunction', {username:'hawk',password:'123456'})
.then(() => logController.getLogs({ from: Date.now() - 500, size: 1000 }))
.then((res) => {
const entry = res[0];
expect(entry.params.password).toMatch(/\*\*\*\*\*\*\*\*/);
Parse.Cloud.run('testFunction', { username: 'hawk', password: '123456' })
.then(() => {
const entry = spy.calls.mostRecent().args;
expect(entry[2].params.password).toMatch(/\*\*\*\*\*\*\*\*/);
done();
})
.then(null, e => done.fail(e));
});
it('should only log once for object not found', async () => {
const config = Config.get('test');
const spy = spyOn(config.loggerController, 'error').and.callThrough();
try {
const object = new Parse.Object('Object');
object.id = 'invalid';
await object.fetch();
} catch (e) {
/**/
}
expect(spy).toHaveBeenCalled();
expect(spy.calls.count()).toBe(1);
const { args } = spy.calls.mostRecent();
expect(args[0]).toBe('Parse error: ');
expect(args[1].message).toBe('Object not found.');
});
});
+37 -25
View File
@@ -1,51 +1,63 @@
var DatabaseController = require('../src/Controllers/DatabaseController.js');
var validateQuery = DatabaseController._validateQuery;
const DatabaseController = require('../lib/Controllers/DatabaseController.js');
const validateQuery = DatabaseController._validateQuery;
describe('DatabaseController', function() {
describe('validateQuery', function() {
it('should restructure simple cases of SERVER-13732', (done) => {
var query = {$or: [{a: 1}, {a: 2}], _rperm: {$in: ['a', 'b']}, foo: 3};
it('should restructure simple cases of SERVER-13732', done => {
const query = {
$or: [{ a: 1 }, { a: 2 }],
_rperm: { $in: ['a', 'b'] },
foo: 3,
};
validateQuery(query);
expect(query).toEqual({$or: [{a: 1, _rperm: {$in: ['a', 'b']}, foo: 3},
{a: 2, _rperm: {$in: ['a', 'b']}, foo: 3}]});
expect(query).toEqual({
$or: [
{ a: 1, _rperm: { $in: ['a', 'b'] }, foo: 3 },
{ a: 2, _rperm: { $in: ['a', 'b'] }, foo: 3 },
],
});
done();
});
it('should not restructure SERVER-13732 queries with $nears', (done) => {
var query = {$or: [{a: 1}, {b: 1}], c: {$nearSphere: {}}};
it('should not restructure SERVER-13732 queries with $nears', done => {
let query = { $or: [{ a: 1 }, { b: 1 }], c: { $nearSphere: {} } };
validateQuery(query);
expect(query).toEqual({$or: [{a: 1}, {b: 1}], c: {$nearSphere: {}}});
expect(query).toEqual({
$or: [{ a: 1 }, { b: 1 }],
c: { $nearSphere: {} },
});
query = {$or: [{a: 1}, {b: 1}], c: {$near: {}}};
query = { $or: [{ a: 1 }, { b: 1 }], c: { $near: {} } };
validateQuery(query);
expect(query).toEqual({$or: [{a: 1}, {b: 1}], c: {$near: {}}});
expect(query).toEqual({ $or: [{ a: 1 }, { b: 1 }], c: { $near: {} } });
done();
});
it('should push refactored keys down a tree for SERVER-13732', (done) => {
var query = {a: 1, $or: [{$or: [{b: 1}, {b: 2}]},
{$or: [{c: 1}, {c: 2}]}]};
it('should push refactored keys down a tree for SERVER-13732', done => {
const query = {
a: 1,
$or: [{ $or: [{ b: 1 }, { b: 2 }] }, { $or: [{ c: 1 }, { c: 2 }] }],
};
validateQuery(query);
expect(query).toEqual({$or: [{$or: [{b: 1, a: 1}, {b: 2, a: 1}]},
{$or: [{c: 1, a: 1}, {c: 2, a: 1}]}]});
expect(query).toEqual({
$or: [
{ $or: [{ b: 1, a: 1 }, { b: 2, a: 1 }] },
{ $or: [{ c: 1, a: 1 }, { c: 2, a: 1 }] },
],
});
done();
});
it('should reject invalid queries', (done) => {
expect(() => validateQuery({$or: {'a': 1}})).toThrow();
it('should reject invalid queries', done => {
expect(() => validateQuery({ $or: { a: 1 } })).toThrow();
done();
});
it('should accept valid queries', (done) => {
expect(() => validateQuery({$or: [{'a': 1}, {'b': 2}]})).not.toThrow();
it('should accept valid queries', done => {
expect(() => validateQuery({ $or: [{ a: 1 }, { b: 2 }] })).not.toThrow();
done();
});
});
});
File diff suppressed because it is too large Load Diff
+63
View File
@@ -0,0 +1,63 @@
const ParseServer = require('../lib/index');
const express = require('express');
const request = require('../lib/request');
describe('Enable express error handler', () => {
it('should call the default handler in case of error, like updating a non existing object', done => {
const serverUrl = 'http://localhost:12667/parse';
const appId = 'anOtherTestApp';
const masterKey = 'anOtherTestMasterKey';
let server;
let lastError;
const parseServer = ParseServer.ParseServer(
Object.assign({}, defaultConfiguration, {
appId: appId,
masterKey: masterKey,
serverURL: serverUrl,
enableExpressErrorHandler: true,
serverStartComplete: () => {
expect(Parse.applicationId).toEqual('anOtherTestApp');
const app = express();
app.use('/parse', parseServer);
server = app.listen(12667);
app.use(function(err, req, res, next) {
next;
lastError = err;
});
request({
method: 'PUT',
url: serverUrl + '/classes/AnyClass/nonExistingId',
headers: {
'X-Parse-Application-Id': appId,
'X-Parse-Master-Key': masterKey,
'Content-Type': 'application/json',
},
body: { someField: 'blablabla' },
})
.then(() => {
fail('Should throw error');
})
.catch(response => {
const reqError = response.data;
expect(reqError).toBeDefined();
expect(lastError).toBeDefined();
expect(lastError.code).toEqual(101);
expect(lastError.message).toEqual('Object not found.');
expect(lastError.code).toEqual(reqError.code);
expect(lastError.message).toEqual(reqError.error);
})
.then(() => {
server.close(done);
});
},
})
);
});
});
+42 -33
View File
@@ -1,49 +1,58 @@
const auth = require('../src/Auth');
const Config = require('../src/Config');
const rest = require('../src/rest');
const auth = require('../lib/Auth');
const Config = require('../lib/Config');
const rest = require('../lib/rest');
describe('Enable single schema cache', () => {
beforeEach((done) => {
beforeEach(done => {
reconfigureServer({
enableSingleSchemaCache: true,
schemaCacheTTL: 30000
schemaCacheTTL: 30000,
}).then(() => {
done();
});
});
it('can perform multiple create and query operations', (done) => {
it('can perform multiple create and query operations', done => {
let config = fakeRequestForConfig();
let nobody = auth.nobody(config);
rest.create(config, nobody, 'Foo', {type: 1}).then(() => {
config = fakeRequestForConfig();
nobody = auth.nobody(config);
return rest.create(config, nobody, 'Foo', {type: 2});
}).then(() => {
config = fakeRequestForConfig();
nobody = auth.nobody(config);
return rest.create(config, nobody, 'Bar');
}).then(() => {
config = fakeRequestForConfig();
nobody = auth.nobody(config);
return rest.find(config, nobody, 'Bar', {type: 1});
}).then(() => {
fail('Should throw error');
done();
}, (error) => {
config = fakeRequestForConfig();
nobody = auth.nobody(config);
expect(error).toBeDefined();
return rest.find(config, nobody, 'Foo', {type: 1});
}).then((response) => {
config = fakeRequestForConfig();
nobody = auth.nobody(config);
expect(response.results.length).toEqual(1);
done();
});
rest
.create(config, nobody, 'Foo', { type: 1 })
.then(() => {
config = fakeRequestForConfig();
nobody = auth.nobody(config);
return rest.create(config, nobody, 'Foo', { type: 2 });
})
.then(() => {
config = fakeRequestForConfig();
nobody = auth.nobody(config);
return rest.create(config, nobody, 'Bar');
})
.then(() => {
config = fakeRequestForConfig();
nobody = auth.nobody(config);
return rest.find(config, nobody, 'Bar', { type: 1 });
})
.then(
() => {
fail('Should throw error');
done();
},
error => {
config = fakeRequestForConfig();
nobody = auth.nobody(config);
expect(error).toBeDefined();
return rest.find(config, nobody, 'Foo', { type: 1 });
}
)
.then(response => {
config = fakeRequestForConfig();
nobody = auth.nobody(config);
expect(response.results.length).toEqual(1);
done();
});
});
});
const fakeRequestForConfig = function() {
return new Config('test');
return Config.get('test');
};
+9 -8
View File
@@ -1,12 +1,13 @@
var EventEmitterPubSub = require('../src/Adapters/PubSub/EventEmitterPubSub').EventEmitterPubSub;
const EventEmitterPubSub = require('../lib/Adapters/PubSub/EventEmitterPubSub')
.EventEmitterPubSub;
describe('EventEmitterPubSub', function() {
it('can publish and subscribe', function() {
var publisher = EventEmitterPubSub.createPublisher();
var subscriber = EventEmitterPubSub.createSubscriber();
const publisher = EventEmitterPubSub.createPublisher();
const subscriber = EventEmitterPubSub.createSubscriber();
subscriber.subscribe('testChannel');
// Register mock checked for subscriber
var isChecked = false;
let isChecked = false;
subscriber.on('message', function(channel, message) {
isChecked = true;
expect(channel).toBe('testChannel');
@@ -19,12 +20,12 @@ describe('EventEmitterPubSub', function() {
});
it('can unsubscribe', function() {
var publisher = EventEmitterPubSub.createPublisher();
var subscriber = EventEmitterPubSub.createSubscriber();
const publisher = EventEmitterPubSub.createPublisher();
const subscriber = EventEmitterPubSub.createSubscriber();
subscriber.subscribe('testChannel');
subscriber.unsubscribe('testChannel');
// Register mock checked for subscriber
var isCalled = false;
let isCalled = false;
subscriber.on('message', function() {
isCalled = true;
});
@@ -35,7 +36,7 @@ describe('EventEmitterPubSub', function() {
});
it('can unsubscribe not subscribing channel', function() {
var subscriber = EventEmitterPubSub.createSubscriber();
const subscriber = EventEmitterPubSub.createSubscriber();
// Make sure subscriber does not throw exception
subscriber.unsubscribe('testChannel');
+89 -33
View File
@@ -1,39 +1,43 @@
const LoggerController = require('../src/Controllers/LoggerController').LoggerController;
const WinstonLoggerAdapter = require('../src/Adapters/Logger/WinstonLoggerAdapter').WinstonLoggerAdapter;
const GridStoreAdapter = require("../src/Adapters/Files/GridStoreAdapter").GridStoreAdapter;
const Config = require("../src/Config");
const FilesController = require('../src/Controllers/FilesController').default;
const LoggerController = require('../lib/Controllers/LoggerController')
.LoggerController;
const WinstonLoggerAdapter = require('../lib/Adapters/Logger/WinstonLoggerAdapter')
.WinstonLoggerAdapter;
const GridFSBucketAdapter = require('../lib/Adapters/Files/GridFSBucketAdapter')
.GridFSBucketAdapter;
const Config = require('../lib/Config');
const FilesController = require('../lib/Controllers/FilesController').default;
const mockAdapter = {
createFile: () => {
return Parse.Promise.reject(new Error('it failed'));
return Promise.reject(new Error('it failed with xyz'));
},
deleteFile: () => { },
getFileData: () => { },
getFileLocation: () => 'xyz'
}
deleteFile: () => {},
getFileData: () => {},
getFileLocation: () => 'xyz',
};
// Small additional tests to improve overall coverage
describe("FilesController",() =>{
it("should properly expand objects", (done) => {
var config = new Config(Parse.applicationId);
var gridStoreAdapter = new GridStoreAdapter('mongodb://localhost:27017/parse');
var filesController = new FilesController(gridStoreAdapter)
var result = filesController.expandFilesInObject(config, function(){});
describe('FilesController', () => {
it('should properly expand objects', done => {
const config = Config.get(Parse.applicationId);
const gridStoreAdapter = new GridFSBucketAdapter(
'mongodb://localhost:27017/parse'
);
const filesController = new FilesController(gridStoreAdapter);
const result = filesController.expandFilesInObject(config, function() {});
expect(result).toBeUndefined();
var fullFile = {
const fullFile = {
type: '__type',
url: "http://an.url"
}
url: 'http://an.url',
};
var anObject = {
aFile: fullFile
}
const anObject = {
aFile: fullFile,
};
filesController.expandFilesInObject(config, anObject);
expect(anObject.aFile.url).toEqual("http://an.url");
expect(anObject.aFile.url).toEqual('http://an.url');
done();
});
@@ -42,24 +46,76 @@ describe("FilesController",() =>{
const logController = new LoggerController(new WinstonLoggerAdapter());
reconfigureServer({ filesAdapter: mockAdapter })
.then(() => new Promise(resolve => setTimeout(resolve, 1000)))
.then(() => new Parse.File("yolo.txt", [1,2,3], "text/plain").save())
.then(() => new Parse.File('yolo.txt', [1, 2, 3], 'text/plain').save())
.then(
() => done.fail('should not succeed'),
() => setImmediate(() => Parse.Promise.as('done'))
() => setImmediate(() => Promise.resolve('done'))
)
.then(() => logController.getLogs({ from: Date.now() - 500, size: 1000 }))
.then((logs) => {
.then(() => new Promise(resolve => setTimeout(resolve, 200)))
.then(() =>
logController.getLogs({ from: Date.now() - 1000, size: 1000 })
)
.then(logs => {
// we get two logs here: 1. the source of the failure to save the file
// and 2 the message that will be sent back to the client.
const log1 = logs.pop();
const log1 = logs.find(
x => x.message === 'Error creating a file: it failed with xyz'
);
expect(log1.level).toBe('error');
expect(log1.message).toBe('it failed');
const log2 = logs.pop();
const log2 = logs.find(
x => x.message === 'Parse error: Could not store file: yolo.txt.'
);
expect(log2.level).toBe('error');
expect(log2.code).toBe(130);
expect(log2.message).toBe('Could not store file.');
done();
});
});
it('should add a unique hash to the file name when the preserveFileName option is false', done => {
const config = Config.get(Parse.applicationId);
const gridStoreAdapter = new GridFSBucketAdapter(
'mongodb://localhost:27017/parse'
);
spyOn(gridStoreAdapter, 'createFile');
gridStoreAdapter.createFile.and.returnValue(Promise.resolve());
const fileName = 'randomFileName.pdf';
const regexEscapedFileName = fileName.replace(/\./g, '\\$&');
const filesController = new FilesController(gridStoreAdapter, null, {
preserveFileName: false,
});
filesController.createFile(config, fileName);
expect(gridStoreAdapter.createFile).toHaveBeenCalledTimes(1);
expect(gridStoreAdapter.createFile.calls.mostRecent().args[0]).toMatch(
`^.{32}_${regexEscapedFileName}$`
);
done();
});
it('should not add a unique hash to the file name when the preserveFileName option is true', done => {
const config = Config.get(Parse.applicationId);
const gridStoreAdapter = new GridFSBucketAdapter(
'mongodb://localhost:27017/parse'
);
spyOn(gridStoreAdapter, 'createFile');
gridStoreAdapter.createFile.and.returnValue(Promise.resolve());
const fileName = 'randomFileName.pdf';
const filesController = new FilesController(gridStoreAdapter, null, {
preserveFileName: true,
});
filesController.createFile(config, fileName);
expect(gridStoreAdapter.createFile).toHaveBeenCalledTimes(1);
expect(gridStoreAdapter.createFile.calls.mostRecent().args[0]).toEqual(
fileName
);
done();
});
});
+63
View File
@@ -0,0 +1,63 @@
const GridStoreAdapter = require('../lib/Adapters/Files/GridStoreAdapter')
.GridStoreAdapter;
const GridFSBucketAdapter = require('../lib/Adapters/Files/GridFSBucketAdapter')
.GridFSBucketAdapter;
const { randomString } = require('../lib/cryptoUtils');
const databaseURI = 'mongodb://localhost:27017/parse';
async function expectMissingFile(gfsAdapter, name) {
try {
await gfsAdapter.getFileData(name);
fail('should have thrown');
} catch (e) {
expect(e.message).toEqual('FileNotFound: file myFileName was not found');
}
}
describe('GridFSBucket and GridStore interop', () => {
beforeEach(async () => {
const gsAdapter = new GridStoreAdapter(databaseURI);
const db = await gsAdapter._connect();
db.dropDatabase();
});
it('a file created in GridStore should be available in GridFS', async () => {
const gsAdapter = new GridStoreAdapter(databaseURI);
const gfsAdapter = new GridFSBucketAdapter(databaseURI);
await expectMissingFile(gfsAdapter, 'myFileName');
const originalString = 'abcdefghi';
await gsAdapter.createFile('myFileName', originalString);
const gsResult = await gsAdapter.getFileData('myFileName');
expect(gsResult.toString('utf8')).toBe(originalString);
const gfsResult = await gfsAdapter.getFileData('myFileName');
expect(gfsResult.toString('utf8')).toBe(originalString);
});
it('properly fetches a large file from GridFS', async () => {
const gfsAdapter = new GridFSBucketAdapter(databaseURI);
const twoMegabytesFile = randomString(2048 * 1024);
await gfsAdapter.createFile('myFileName', twoMegabytesFile);
const gfsResult = await gfsAdapter.getFileData('myFileName');
expect(gfsResult.toString('utf8')).toBe(twoMegabytesFile);
});
it('properly deletes a file from GridFS', async () => {
const gfsAdapter = new GridFSBucketAdapter(databaseURI);
await gfsAdapter.createFile('myFileName', 'a simple file');
await gfsAdapter.deleteFile('myFileName');
await expectMissingFile(gfsAdapter, 'myFileName');
}, 1000000);
it('properly overrides files', async () => {
const gfsAdapter = new GridFSBucketAdapter(databaseURI);
await gfsAdapter.createFile('myFileName', 'a simple file');
await gfsAdapter.createFile('myFileName', 'an overrided simple file');
const data = await gfsAdapter.getFileData('myFileName');
expect(data.toString('utf8')).toBe('an overrided simple file');
const bucket = await gfsAdapter._getBucket();
const documents = await bucket.find({ filename: 'myFileName' }).toArray();
expect(documents.length).toBe(2);
await gfsAdapter.deleteFile('myFileName');
await expectMissingFile(gfsAdapter, 'myFileName');
});
});
-87
View File
@@ -1,87 +0,0 @@
var MongoClient = require("mongodb").MongoClient;
var GridStore = require("mongodb").GridStore;
var GridStoreAdapter = require("../src/Adapters/Files/GridStoreAdapter").GridStoreAdapter;
var Config = require("../src/Config");
var FilesController = require('../src/Controllers/FilesController').default;
// Small additional tests to improve overall coverage
describe_only_db('mongo')("GridStoreAdapter",() =>{
it("should properly instanciate the GridStore when deleting a file", (done) => {
var databaseURI = 'mongodb://localhost:27017/parse';
var config = new Config(Parse.applicationId);
var gridStoreAdapter = new GridStoreAdapter(databaseURI);
var filesController = new FilesController(gridStoreAdapter);
// save original unlink before redefinition
var originalUnlink = GridStore.prototype.unlink;
var gridStoreMode;
// new unlink method that will capture the mode in which GridStore was opened
GridStore.prototype.unlink = function() {
// restore original unlink during first call
GridStore.prototype.unlink = originalUnlink;
gridStoreMode = this.mode;
return originalUnlink.call(this);
};
filesController.createFile(config, 'myFilename.txt', 'my file content', 'text/plain')
.then(myFile => {
return MongoClient.connect(databaseURI)
.then(database => {
// Verify the existance of the fs.files document
return database.collection('fs.files').count().then(count => {
expect(count).toEqual(1);
return database;
});
})
.then(database => {
// Verify the existance of the fs.files document
return database.collection('fs.chunks').count().then(count => {
expect(count).toEqual(1);
return database.close();
});
})
.then(() => {
return filesController.deleteFile(config, myFile.name);
});
})
.then(() => {
return MongoClient.connect(databaseURI)
.then(database => {
// Verify the existance of the fs.files document
return database.collection('fs.files').count().then(count => {
expect(count).toEqual(0);
return database;
});
})
.then(database => {
// Verify the existance of the fs.files document
return database.collection('fs.chunks').count().then(count => {
expect(count).toEqual(0);
return database.close();
});
});
})
.then(() => {
// Verify that gridStore was opened in read only mode
expect(gridStoreMode).toEqual('r');
done();
})
.catch(fail);
})
});
+99
View File
@@ -0,0 +1,99 @@
const MongoClient = require('mongodb').MongoClient;
const GridStore = require('mongodb').GridStore;
const GridStoreAdapter = require('../lib/Adapters/Files/GridStoreAdapter')
.GridStoreAdapter;
const Config = require('../lib/Config');
const FilesController = require('../lib/Controllers/FilesController').default;
// Small additional tests to improve overall coverage
describe_only_db('mongo')('GridStoreAdapter', () => {
it('should properly instanciate the GridStore when deleting a file', async done => {
const databaseURI = 'mongodb://localhost:27017/parse';
const config = Config.get(Parse.applicationId);
const gridStoreAdapter = new GridStoreAdapter(databaseURI);
const db = await gridStoreAdapter._connect();
db.dropDatabase();
const filesController = new FilesController(
gridStoreAdapter,
Parse.applicationId,
{}
);
// save original unlink before redefinition
const originalUnlink = GridStore.prototype.unlink;
let gridStoreMode;
// new unlink method that will capture the mode in which GridStore was opened
GridStore.prototype.unlink = function() {
// restore original unlink during first call
GridStore.prototype.unlink = originalUnlink;
gridStoreMode = this.mode;
return originalUnlink.call(this);
};
filesController
.createFile(config, 'myFilename.txt', 'my file content', 'text/plain')
.then(myFile => {
return MongoClient.connect(databaseURI)
.then(client => {
const database = client.db(client.s.options.dbName);
// Verify the existance of the fs.files document
return database
.collection('fs.files')
.count()
.then(count => {
expect(count).toEqual(1);
return { database, client };
});
})
.then(({ database, client }) => {
// Verify the existance of the fs.files document
return database
.collection('fs.chunks')
.count()
.then(count => {
expect(count).toEqual(1);
return client.close();
});
})
.then(() => {
return filesController.deleteFile(config, myFile.name);
});
})
.then(() => {
return MongoClient.connect(databaseURI)
.then(client => {
const database = client.db(client.s.options.dbName);
// Verify the existance of the fs.files document
return database
.collection('fs.files')
.count()
.then(count => {
expect(count).toEqual(0);
return { database, client };
});
})
.then(({ database, client }) => {
// Verify the existance of the fs.files document
return database
.collection('fs.chunks')
.count()
.then(count => {
expect(count).toEqual(0);
return client.close();
});
});
})
.then(() => {
// Verify that gridStore was opened in read only mode
expect(gridStoreMode).toEqual('r');
done();
})
.catch(fail);
});
});
+148 -181
View File
@@ -1,116 +1,97 @@
'use strict';
var httpRequest = require("../src/cloud-code/httpRequest"),
HTTPResponse = require('../src/cloud-code/HTTPResponse').default,
const httpRequest = require('../lib/cloud-code/httpRequest'),
HTTPResponse = require('../lib/cloud-code/HTTPResponse').default,
bodyParser = require('body-parser'),
express = require("express");
express = require('express');
var port = 13371;
var httpRequestServer = "http://localhost:" + port;
const port = 13371;
const httpRequestServer = 'http://localhost:' + port;
var app = express();
app.use(bodyParser.json({ 'type': '*/*' }));
app.get("/hello", function(req, res){
res.json({response: "OK"});
});
function startServer(done) {
const app = express();
app.use(bodyParser.json({ type: '*/*' }));
app.get('/hello', function(req, res) {
res.json({ response: 'OK' });
});
app.get("/404", function(req, res){
res.status(404);
res.send("NO");
});
app.get('/404', function(req, res) {
res.status(404);
res.send('NO');
});
app.get("/301", function(req, res){
res.status(301);
res.location("/hello");
res.send();
});
app.get('/301', function(req, res) {
res.status(301);
res.location('/hello');
res.send();
});
app.post('/echo', function(req, res){
res.json(req.body);
});
app.post('/echo', function(req, res) {
res.json(req.body);
});
app.get('/qs', function(req, res){
res.json(req.query);
});
app.get('/qs', function(req, res) {
res.json(req.query);
});
app.listen(13371);
return app.listen(13371, undefined, done);
}
describe('httpRequest', () => {
let server;
beforeAll(done => {
server = startServer(done);
});
describe("httpRequest", () => {
it("should do /hello", (done) => {
afterAll(done => {
server.close(done);
});
it('should do /hello', done => {
httpRequest({
url: httpRequestServer + "/hello"
}).then(function(httpResponse){
url: httpRequestServer + '/hello',
}).then(function(httpResponse) {
expect(httpResponse.status).toBe(200);
expect(httpResponse.buffer).toEqual(new Buffer('{"response":"OK"}'));
expect(httpResponse.text).toEqual('{"response":"OK"}');
expect(httpResponse.data.response).toEqual("OK");
expect(httpResponse.data.response).toEqual('OK');
done();
}, function(){
fail("should not fail");
done();
})
}, done.fail);
});
it("should do /hello with callback and promises", (done) => {
var calls = 0;
it('should do not follow redirects by default', done => {
httpRequest({
url: httpRequestServer + "/hello",
success: function() { calls++; },
error: function() { calls++; }
}).then(function(httpResponse){
expect(calls).toBe(1);
expect(httpResponse.status).toBe(200);
expect(httpResponse.buffer).toEqual(new Buffer('{"response":"OK"}'));
expect(httpResponse.text).toEqual('{"response":"OK"}');
expect(httpResponse.data.response).toEqual("OK");
done();
}, function(){
fail("should not fail");
done();
})
});
it("should do not follow redirects by default", (done) => {
httpRequest({
url: httpRequestServer + "/301"
}).then(function(httpResponse){
url: httpRequestServer + '/301',
}).then(function(httpResponse) {
expect(httpResponse.status).toBe(301);
done();
}, function(){
fail("should not fail");
done();
})
}, done.fail);
});
it("should follow redirects when set", (done) => {
it('should follow redirects when set', done => {
httpRequest({
url: httpRequestServer + "/301",
followRedirects: true
}).then(function(httpResponse){
url: httpRequestServer + '/301',
followRedirects: true,
}).then(function(httpResponse) {
expect(httpResponse.status).toBe(200);
expect(httpResponse.buffer).toEqual(new Buffer('{"response":"OK"}'));
expect(httpResponse.text).toEqual('{"response":"OK"}');
expect(httpResponse.data.response).toEqual("OK");
expect(httpResponse.data.response).toEqual('OK');
done();
}, function(){
fail("should not fail");
done();
})
}, done.fail);
});
it("should fail on 404", (done) => {
var calls = 0;
it('should fail on 404', done => {
let calls = 0;
httpRequest({
url: httpRequestServer + "/404",
success: function() {
url: httpRequestServer + '/404',
}).then(
function() {
calls++;
fail("should not succeed");
fail('should not succeed');
done();
},
error: function(httpResponse) {
function(httpResponse) {
calls++;
expect(calls).toBe(1);
expect(httpResponse.status).toBe(404);
@@ -119,131 +100,116 @@ describe("httpRequest", () => {
expect(httpResponse.data).toBe(undefined);
done();
}
});
})
it("should fail on 404", (done) => {
httpRequest({
url: httpRequestServer + "/404",
}).then(function(){
fail("should not succeed");
done();
}, function(httpResponse){
expect(httpResponse.status).toBe(404);
expect(httpResponse.buffer).toEqual(new Buffer('NO'));
expect(httpResponse.text).toEqual('NO');
expect(httpResponse.data).toBe(undefined);
done();
})
})
it("should post on echo", (done) => {
var calls = 0;
httpRequest({
method: "POST",
url: httpRequestServer + "/echo",
body: {
foo: "bar"
},
headers: {
'Content-Type': 'application/json'
},
success: function() { calls++; },
error: function() { calls++; }
}).then(function(httpResponse){
expect(calls).toBe(1);
expect(httpResponse.status).toBe(200);
expect(httpResponse.data).toEqual({foo: "bar"});
done();
}, function(){
fail("should not fail");
done();
})
);
});
it("should encode a query string body by default", (done) => {
it('should post on echo', done => {
httpRequest({
method: 'POST',
url: httpRequestServer + '/echo',
body: {
foo: 'bar',
},
headers: {
'Content-Type': 'application/json',
},
}).then(
function(httpResponse) {
expect(httpResponse.status).toBe(200);
expect(httpResponse.data).toEqual({ foo: 'bar' });
done();
},
function() {
fail('should not fail');
done();
}
);
});
it('should encode a query string body by default', done => {
const options = {
body: {"foo": "bar"},
}
body: { foo: 'bar' },
};
const result = httpRequest.encodeBody(options);
expect(result.body).toEqual('foo=bar');
expect(result.headers['Content-Type']).toEqual('application/x-www-form-urlencoded');
expect(result.headers['Content-Type']).toEqual(
'application/x-www-form-urlencoded'
);
done();
});
})
it("should encode a JSON body", (done) => {
it('should encode a JSON body', done => {
const options = {
body: {"foo": "bar"},
headers: {'Content-Type': 'application/json'}
}
body: { foo: 'bar' },
headers: { 'Content-Type': 'application/json' },
};
const result = httpRequest.encodeBody(options);
expect(result.body).toEqual('{"foo":"bar"}');
done();
})
it("should encode a www-form body", (done) => {
});
it('should encode a www-form body', done => {
const options = {
body: {"foo": "bar", "bar": "baz"},
headers: {'cOntent-tYpe': 'application/x-www-form-urlencoded'}
}
body: { foo: 'bar', bar: 'baz' },
headers: { 'cOntent-tYpe': 'application/x-www-form-urlencoded' },
};
const result = httpRequest.encodeBody(options);
expect(result.body).toEqual("foo=bar&bar=baz");
expect(result.body).toEqual('foo=bar&bar=baz');
done();
});
it("should not encode a wrong content type", (done) => {
it('should not encode a wrong content type', done => {
const options = {
body:{"foo": "bar", "bar": "baz"},
headers: {'cOntent-tYpe': 'mime/jpeg'}
}
body: { foo: 'bar', bar: 'baz' },
headers: { 'cOntent-tYpe': 'mime/jpeg' },
};
const result = httpRequest.encodeBody(options);
expect(result.body).toEqual({"foo": "bar", "bar": "baz"});
expect(result.body).toEqual({ foo: 'bar', bar: 'baz' });
done();
});
it("should fail gracefully", (done) => {
it('should fail gracefully', done => {
httpRequest({
url: "http://not a good url",
success: function() {
fail("should not succeed");
done();
},
error: function(error) {
expect(error).not.toBeUndefined();
expect(error).not.toBeNull();
done();
}
url: 'http://not a good url',
}).then(done.fail, function(error) {
expect(error).not.toBeUndefined();
expect(error).not.toBeNull();
done();
});
});
it("should params object to query string", (done) => {
it('should params object to query string', done => {
httpRequest({
url: httpRequestServer + "/qs",
url: httpRequestServer + '/qs',
params: {
foo: "bar"
foo: 'bar',
},
}).then(
function(httpResponse) {
expect(httpResponse.status).toBe(200);
expect(httpResponse.data).toEqual({ foo: 'bar' });
done();
},
function() {
fail('should not fail');
done();
}
}).then(function(httpResponse){
expect(httpResponse.status).toBe(200);
expect(httpResponse.data).toEqual({foo: "bar"});
done();
}, function(){
fail("should not fail");
done();
})
);
});
it("should params string to query string", (done) => {
it('should params string to query string', done => {
httpRequest({
url: httpRequestServer + "/qs",
params: "foo=bar&foo2=bar2"
}).then(function(httpResponse){
expect(httpResponse.status).toBe(200);
expect(httpResponse.data).toEqual({foo: "bar", foo2: 'bar2'});
done();
}, function(){
fail("should not fail");
done();
})
url: httpRequestServer + '/qs',
params: 'foo=bar&foo2=bar2',
}).then(
function(httpResponse) {
expect(httpResponse.status).toBe(200);
expect(httpResponse.data).toEqual({ foo: 'bar', foo2: 'bar2' });
done();
},
function() {
fail('should not fail');
done();
}
);
});
it('should not crash with undefined body', () => {
@@ -268,17 +234,17 @@ describe("httpRequest", () => {
});
it('serialized httpResponse correctly with body object', () => {
const httpResponse = new HTTPResponse({}, {foo: "bar"});
const httpResponse = new HTTPResponse({}, { foo: 'bar' });
Parse._encode(httpResponse);
const serialized = JSON.stringify(httpResponse);
const result = JSON.parse(serialized);
expect(httpResponse.text).toEqual('{"foo":"bar"}');
expect(httpResponse.data).toEqual({foo: 'bar'});
expect(httpResponse.body).toEqual({foo: 'bar'});
expect(httpResponse.data).toEqual({ foo: 'bar' });
expect(httpResponse.body).toEqual({ foo: 'bar' });
expect(result.text).toEqual('{"foo":"bar"}');
expect(result.data).toEqual({foo: 'bar'});
expect(result.data).toEqual({ foo: 'bar' });
expect(result.body).toEqual(undefined);
});
@@ -299,15 +265,17 @@ describe("httpRequest", () => {
const serialized = JSON.stringify(httpResponse);
const result = JSON.parse(serialized);
expect(result.text).toEqual('{"foo":"bar"}');
expect(result.data).toEqual({foo: 'bar'});
expect(result.data).toEqual({ foo: 'bar' });
});
it('serialized httpResponse with Parse._encode should be allright', () => {
const json = '{"foo":"bar"}';
const httpResponse = new HTTPResponse({}, new Buffer(json));
const encoded = Parse._encode(httpResponse);
let foundData, foundText, foundBody = false;
for(var key in encoded) {
let foundData,
foundText,
foundBody = false;
for (const key in encoded) {
if (key == 'data') {
foundData = true;
}
@@ -322,5 +290,4 @@ describe("httpRequest", () => {
expect(foundText).toBe(true);
expect(foundBody).toBe(false);
});
});
+19 -22
View File
@@ -1,42 +1,40 @@
const InMemoryCache = require('../src/Adapters/Cache/InMemoryCache').default;
const InMemoryCache = require('../lib/Adapters/Cache/InMemoryCache').default;
describe('InMemoryCache', function() {
var BASE_TTL = {
ttl: 100
const BASE_TTL = {
ttl: 100,
};
var NO_EXPIRE_TTL = {
ttl: NaN
const NO_EXPIRE_TTL = {
ttl: NaN,
};
var KEY = 'hello';
var KEY_2 = KEY + '_2';
var VALUE = 'world';
const KEY = 'hello';
const KEY_2 = KEY + '_2';
const VALUE = 'world';
function wait(sleep) {
return new Promise(function(resolve) {
setTimeout(resolve, sleep);
})
});
}
it('should destroy a expire items in the cache', (done) => {
var cache = new InMemoryCache(BASE_TTL);
it('should destroy a expire items in the cache', done => {
const cache = new InMemoryCache(BASE_TTL);
cache.put(KEY, VALUE);
var value = cache.get(KEY);
let value = cache.get(KEY);
expect(value).toEqual(VALUE);
wait(BASE_TTL.ttl * 2).then(() => {
value = cache.get(KEY)
wait(BASE_TTL.ttl * 10).then(() => {
value = cache.get(KEY);
expect(value).toEqual(null);
done();
});
});
it('should delete items', (done) => {
var cache = new InMemoryCache(NO_EXPIRE_TTL);
it('should delete items', done => {
const cache = new InMemoryCache(NO_EXPIRE_TTL);
cache.put(KEY, VALUE);
cache.put(KEY_2, VALUE);
expect(cache.get(KEY)).toEqual(VALUE);
@@ -52,8 +50,8 @@ describe('InMemoryCache', function() {
done();
});
it('should clear all items', (done) => {
var cache = new InMemoryCache(NO_EXPIRE_TTL);
it('should clear all items', done => {
const cache = new InMemoryCache(NO_EXPIRE_TTL);
cache.put(KEY, VALUE);
cache.put(KEY_2, VALUE);
@@ -67,8 +65,7 @@ describe('InMemoryCache', function() {
});
it('should deafult TTL to 5 seconds', () => {
var cache = new InMemoryCache({});
const cache = new InMemoryCache({});
expect(cache.ttl).toEqual(5 * 1000);
});
});
+24 -22
View File
@@ -1,18 +1,19 @@
var InMemoryCacheAdapter = require('../src/Adapters/Cache/InMemoryCacheAdapter').default;
const InMemoryCacheAdapter = require('../lib/Adapters/Cache/InMemoryCacheAdapter')
.default;
describe('InMemoryCacheAdapter', function() {
var KEY = 'hello';
var VALUE = 'world';
const KEY = 'hello';
const VALUE = 'world';
function wait(sleep) {
return new Promise(function(resolve) {
setTimeout(resolve, sleep);
})
});
}
it('should expose promisifyed methods', (done) => {
var cache = new InMemoryCacheAdapter({
ttl: NaN
it('should expose promisifyed methods', done => {
const cache = new InMemoryCacheAdapter({
ttl: NaN,
});
// Verify all methods return promises.
@@ -20,38 +21,39 @@ describe('InMemoryCacheAdapter', function() {
cache.put(KEY, VALUE),
cache.del(KEY),
cache.get(KEY),
cache.clear()
cache.clear(),
]).then(() => {
done();
});
});
it('should get/set/clear', (done) => {
var cache = new InMemoryCacheAdapter({
ttl: NaN
it('should get/set/clear', done => {
const cache = new InMemoryCacheAdapter({
ttl: NaN,
});
cache.put(KEY, VALUE)
cache
.put(KEY, VALUE)
.then(() => cache.get(KEY))
.then((value) => expect(value).toEqual(VALUE))
.then(value => expect(value).toEqual(VALUE))
.then(() => cache.clear())
.then(() => cache.get(KEY))
.then((value) => expect(value).toEqual(null))
.then(value => expect(value).toEqual(null))
.then(done);
});
it('should expire after ttl', (done) => {
var cache = new InMemoryCacheAdapter({
ttl: 10
it('should expire after ttl', done => {
const cache = new InMemoryCacheAdapter({
ttl: 10,
});
cache.put(KEY, VALUE)
cache
.put(KEY, VALUE)
.then(() => cache.get(KEY))
.then((value) => expect(value).toEqual(VALUE))
.then(value => expect(value).toEqual(VALUE))
.then(wait.bind(null, 50))
.then(() => cache.get(KEY))
.then((value) => expect(value).toEqual(null))
.then(value => expect(value).toEqual(null))
.then(done);
})
});
});
+225 -105
View File
@@ -1,148 +1,204 @@
var auth = require('../src/Auth');
var Config = require('../src/Config');
var rest = require('../src/rest');
var InstallationsRouter = require('../src/Routers/InstallationsRouter').InstallationsRouter;
const auth = require('../lib/Auth');
const Config = require('../lib/Config');
const rest = require('../lib/rest');
const InstallationsRouter = require('../lib/Routers/InstallationsRouter')
.InstallationsRouter;
describe('InstallationsRouter', () => {
it('uses find condition from request.body', (done) => {
var config = new Config('test');
var androidDeviceRequest = {
'installationId': '12345678-abcd-abcd-abcd-123456789abc',
'deviceType': 'android'
it('uses find condition from request.body', done => {
const config = Config.get('test');
const androidDeviceRequest = {
installationId: '12345678-abcd-abcd-abcd-123456789abc',
deviceType: 'android',
};
var iosDeviceRequest = {
'installationId': '12345678-abcd-abcd-abcd-123456789abd',
'deviceType': 'ios'
const iosDeviceRequest = {
installationId: '12345678-abcd-abcd-abcd-123456789abd',
deviceType: 'ios',
};
var request = {
const request = {
config: config,
auth: auth.master(config),
body: {
where: {
deviceType: 'android'
}
deviceType: 'android',
},
},
query: {},
info: {}
info: {},
};
var router = new InstallationsRouter();
rest.create(config, auth.nobody(config), '_Installation', androidDeviceRequest)
const router = new InstallationsRouter();
rest
.create(
config,
auth.nobody(config),
'_Installation',
androidDeviceRequest
)
.then(() => {
return rest.create(
config,
auth.nobody(config),
'_Installation',
iosDeviceRequest
);
})
.then(() => {
return rest.create(config, auth.nobody(config), '_Installation', iosDeviceRequest);
}).then(() => {
return router.handleFind(request);
}).then((res) => {
var results = res.response.results;
})
.then(res => {
const results = res.response.results;
expect(results.length).toEqual(1);
done();
}).catch((err) => {
})
.catch(err => {
fail(JSON.stringify(err));
done();
});
});
it('uses find condition from request.query', (done) => {
var config = new Config('test');
var androidDeviceRequest = {
'installationId': '12345678-abcd-abcd-abcd-123456789abc',
'deviceType': 'android'
it('uses find condition from request.query', done => {
const config = Config.get('test');
const androidDeviceRequest = {
installationId: '12345678-abcd-abcd-abcd-123456789abc',
deviceType: 'android',
};
var iosDeviceRequest = {
'installationId': '12345678-abcd-abcd-abcd-123456789abd',
'deviceType': 'ios'
const iosDeviceRequest = {
installationId: '12345678-abcd-abcd-abcd-123456789abd',
deviceType: 'ios',
};
var request = {
const request = {
config: config,
auth: auth.master(config),
body: {},
query: {
where: {
deviceType: 'android'
}
deviceType: 'android',
},
},
info: {}
info: {},
};
var router = new InstallationsRouter();
rest.create(config, auth.nobody(config), '_Installation', androidDeviceRequest)
const router = new InstallationsRouter();
rest
.create(
config,
auth.nobody(config),
'_Installation',
androidDeviceRequest
)
.then(() => {
return rest.create(
config,
auth.nobody(config),
'_Installation',
iosDeviceRequest
);
})
.then(() => {
return rest.create(config, auth.nobody(config), '_Installation', iosDeviceRequest);
}).then(() => {
return router.handleFind(request);
}).then((res) => {
var results = res.response.results;
})
.then(res => {
const results = res.response.results;
expect(results.length).toEqual(1);
done();
}).catch((err) => {
})
.catch(err => {
jfail(err);
done();
});
});
it('query installations with limit = 0', (done) => {
var config = new Config('test');
var androidDeviceRequest = {
'installationId': '12345678-abcd-abcd-abcd-123456789abc',
'deviceType': 'android'
it('query installations with limit = 0', done => {
const config = Config.get('test');
const androidDeviceRequest = {
installationId: '12345678-abcd-abcd-abcd-123456789abc',
deviceType: 'android',
};
var iosDeviceRequest = {
'installationId': '12345678-abcd-abcd-abcd-123456789abd',
'deviceType': 'ios'
const iosDeviceRequest = {
installationId: '12345678-abcd-abcd-abcd-123456789abd',
deviceType: 'ios',
};
var request = {
const request = {
config: config,
auth: auth.master(config),
body: {},
query: {
limit: 0
limit: 0,
},
info: {}
info: {},
};
new Config('test');
var router = new InstallationsRouter();
rest.create(config, auth.nobody(config), '_Installation', androidDeviceRequest)
Config.get('test');
const router = new InstallationsRouter();
rest
.create(
config,
auth.nobody(config),
'_Installation',
androidDeviceRequest
)
.then(() => {
return rest.create(
config,
auth.nobody(config),
'_Installation',
iosDeviceRequest
);
})
.then(() => {
return rest.create(config, auth.nobody(config), '_Installation', iosDeviceRequest);
}).then(() => {
return router.handleFind(request);
}).then((res) => {
var response = res.response;
})
.then(res => {
const response = res.response;
expect(response.results.length).toEqual(0);
done();
}).catch((err) => {
})
.catch(err => {
fail(JSON.stringify(err));
done();
});
});
it('query installations with count = 1', done => {
var config = new Config('test');
var androidDeviceRequest = {
'installationId': '12345678-abcd-abcd-abcd-123456789abc',
'deviceType': 'android'
it_exclude_dbs(['postgres'])('query installations with count = 1', done => {
const config = Config.get('test');
const androidDeviceRequest = {
installationId: '12345678-abcd-abcd-abcd-123456789abc',
deviceType: 'android',
};
var iosDeviceRequest = {
'installationId': '12345678-abcd-abcd-abcd-123456789abd',
'deviceType': 'ios'
const iosDeviceRequest = {
installationId: '12345678-abcd-abcd-abcd-123456789abd',
deviceType: 'ios',
};
var request = {
const request = {
config: config,
auth: auth.master(config),
body: {},
query: {
count: 1
count: 1,
},
info: {}
info: {},
};
var router = new InstallationsRouter();
rest.create(config, auth.nobody(config), '_Installation', androidDeviceRequest)
.then(() => rest.create(config, auth.nobody(config), '_Installation', iosDeviceRequest))
const router = new InstallationsRouter();
rest
.create(
config,
auth.nobody(config),
'_Installation',
androidDeviceRequest
)
.then(() =>
rest.create(
config,
auth.nobody(config),
'_Installation',
iosDeviceRequest
)
)
.then(() => router.handleFind(request))
.then((res) => {
var response = res.response;
.then(res => {
const response = res.response;
expect(response.results.length).toEqual(2);
expect(response.count).toEqual(2);
done();
@@ -150,44 +206,108 @@ describe('InstallationsRouter', () => {
.catch(error => {
fail(JSON.stringify(error));
done();
})
});
});
it('query installations with limit = 0 and count = 1', (done) => {
var config = new Config('test');
var androidDeviceRequest = {
'installationId': '12345678-abcd-abcd-abcd-123456789abc',
'deviceType': 'android'
it_only_db('postgres')('query installations with count = 1', async () => {
const config = Config.get('test');
const androidDeviceRequest = {
installationId: '12345678-abcd-abcd-abcd-123456789abc',
deviceType: 'android',
};
var iosDeviceRequest = {
'installationId': '12345678-abcd-abcd-abcd-123456789abd',
'deviceType': 'ios'
const iosDeviceRequest = {
installationId: '12345678-abcd-abcd-abcd-123456789abd',
deviceType: 'ios',
};
var request = {
const request = {
config: config,
auth: auth.master(config),
body: {},
query: {
limit: 0,
count: 1
count: 1,
},
info: {}
info: {},
};
var router = new InstallationsRouter();
rest.create(config, auth.nobody(config), '_Installation', androidDeviceRequest)
.then(() => {
return rest.create(config, auth.nobody(config), '_Installation', iosDeviceRequest);
}).then(() => {
return router.handleFind(request);
}).then((res) => {
var response = res.response;
expect(response.results.length).toEqual(0);
expect(response.count).toEqual(2);
done();
}).catch((err) => {
fail(JSON.stringify(err));
done();
});
const router = new InstallationsRouter();
await rest.create(
config,
auth.nobody(config),
'_Installation',
androidDeviceRequest
);
await rest.create(
config,
auth.nobody(config),
'_Installation',
iosDeviceRequest
);
let res = await router.handleFind(request);
let response = res.response;
expect(response.results.length).toEqual(2);
expect(response.count).toEqual(0); // estimate count is zero
const pgAdapter = config.database.adapter;
await pgAdapter.updateEstimatedCount('_Installation');
res = await router.handleFind(request);
response = res.response;
expect(response.results.length).toEqual(2);
expect(response.count).toEqual(2);
});
it_exclude_dbs(['postgres'])(
'query installations with limit = 0 and count = 1',
done => {
const config = Config.get('test');
const androidDeviceRequest = {
installationId: '12345678-abcd-abcd-abcd-123456789abc',
deviceType: 'android',
};
const iosDeviceRequest = {
installationId: '12345678-abcd-abcd-abcd-123456789abd',
deviceType: 'ios',
};
const request = {
config: config,
auth: auth.master(config),
body: {},
query: {
limit: 0,
count: 1,
},
info: {},
};
const router = new InstallationsRouter();
rest
.create(
config,
auth.nobody(config),
'_Installation',
androidDeviceRequest
)
.then(() => {
return rest.create(
config,
auth.nobody(config),
'_Installation',
iosDeviceRequest
);
})
.then(() => {
return router.handleFind(request);
})
.then(res => {
const response = res.response;
expect(response.results.length).toEqual(0);
expect(response.count).toEqual(2);
done();
})
.catch(err => {
fail(JSON.stringify(err));
done();
});
}
);
});
+190 -102
View File
@@ -1,198 +1,286 @@
const rp = require('request-promise');
const request = require('../lib/request');
const defaultHeaders = {
'X-Parse-Application-Id': 'test',
'X-Parse-Rest-API-Key': 'rest'
}
'X-Parse-Rest-API-Key': 'rest',
'Content-Type': 'application/json',
};
const masterKeyHeaders = {
'X-Parse-Application-Id': 'test',
'X-Parse-Rest-API-Key': 'rest',
'X-Parse-Master-Key': 'test'
}
'X-Parse-Master-Key': 'test',
'Content-Type': 'application/json',
};
const defaultOptions = {
headers: defaultHeaders,
json: true
}
json: true,
};
const masterKeyOptions = {
headers: masterKeyHeaders,
json: true
}
json: true,
};
describe('JobSchedule', () => {
it('should create _JobSchedule with masterKey', (done) => {
it('should create _JobSchedule with masterKey', done => {
const jobSchedule = new Parse.Object('_JobSchedule');
jobSchedule.set({
'jobName': 'MY Cool Job'
jobName: 'MY Cool Job',
});
jobSchedule.save(null, {useMasterKey: true}).then(() => {
done();
})
jobSchedule
.save(null, { useMasterKey: true })
.then(() => {
done();
})
.catch(done.fail);
});
it('should fail creating _JobSchedule without masterKey', (done) => {
it('should fail creating _JobSchedule without masterKey', done => {
const jobSchedule = new Parse.Object('_JobSchedule');
jobSchedule.set({
'jobName': 'SomeJob'
jobName: 'SomeJob',
});
jobSchedule.save(null).then(done.fail)
.catch(done);
jobSchedule
.save(null)
.then(done.fail)
.catch(() => done());
});
it('should reject access when not using masterKey (/jobs)', (done) => {
rp.get(Parse.serverURL + '/cloud_code/jobs', defaultOptions).then(done.fail, done);
it('should reject access when not using masterKey (/jobs)', done => {
request(
Object.assign(
{ url: Parse.serverURL + '/cloud_code/jobs' },
defaultOptions
)
).then(done.fail, () => done());
});
it('should reject access when not using masterKey (/jobs/data)', (done) => {
rp.get(Parse.serverURL + '/cloud_code/jobs/data', defaultOptions).then(done.fail, done);
it('should reject access when not using masterKey (/jobs/data)', done => {
request(
Object.assign(
{ url: Parse.serverURL + '/cloud_code/jobs/data' },
defaultOptions
)
).then(done.fail, () => done());
});
it('should reject access when not using masterKey (PUT /jobs/id)', (done) => {
rp.put(Parse.serverURL + '/cloud_code/jobs/jobId', defaultOptions).then(done.fail, done);
it('should reject access when not using masterKey (PUT /jobs/id)', done => {
request(
Object.assign(
{ method: 'PUT', url: Parse.serverURL + '/cloud_code/jobs/jobId' },
defaultOptions
)
).then(done.fail, () => done());
});
it('should reject access when not using masterKey (PUT /jobs/id)', (done) => {
rp.del(Parse.serverURL + '/cloud_code/jobs/jobId', defaultOptions).then(done.fail, done);
it('should reject access when not using masterKey (DELETE /jobs/id)', done => {
request(
Object.assign(
{ method: 'DELETE', url: Parse.serverURL + '/cloud_code/jobs/jobId' },
defaultOptions
)
).then(done.fail, () => done());
});
it('should allow access when using masterKey (/jobs)', (done) => {
rp.get(Parse.serverURL + '/cloud_code/jobs', masterKeyOptions).then(done, done.fail);
it('should allow access when using masterKey (GET /jobs)', done => {
request(
Object.assign(
{ url: Parse.serverURL + '/cloud_code/jobs' },
masterKeyOptions
)
).then(done, done.fail);
});
it('should create a job schedule', (done) => {
it('should create a job schedule', done => {
Parse.Cloud.job('job', () => {});
const options = Object.assign({}, masterKeyOptions, {
method: 'POST',
url: Parse.serverURL + '/cloud_code/jobs',
body: {
job_schedule: {
jobName: 'job'
}
}
jobName: 'job',
},
},
});
rp.post(Parse.serverURL + '/cloud_code/jobs', options).then((res) => {
expect(res.objectId).not.toBeUndefined();
})
.then(() => {
return rp.get(Parse.serverURL + '/cloud_code/jobs', masterKeyOptions);
request(options)
.then(res => {
expect(res.data.objectId).not.toBeUndefined();
})
.then((res) => {
expect(res.length).toBe(1);
.then(() => {
return request(
Object.assign(
{ url: Parse.serverURL + '/cloud_code/jobs' },
masterKeyOptions
)
);
})
.then(res => {
expect(res.data.length).toBe(1);
})
.then(done)
.catch(done.fail);
});
it('should fail creating a job with an invalid name', (done) => {
it('should fail creating a job with an invalid name', done => {
const options = Object.assign({}, masterKeyOptions, {
url: Parse.serverURL + '/cloud_code/jobs',
method: 'POST',
body: {
job_schedule: {
jobName: 'job'
}
}
jobName: 'job',
},
},
});
rp.post(Parse.serverURL + '/cloud_code/jobs', options)
request(options)
.then(done.fail)
.catch(done);
.catch(() => done());
});
it('should update a job', (done) => {
it('should update a job', done => {
Parse.Cloud.job('job1', () => {});
Parse.Cloud.job('job2', () => {});
const options = Object.assign({}, masterKeyOptions, {
method: 'POST',
url: Parse.serverURL + '/cloud_code/jobs',
body: {
job_schedule: {
jobName: 'job1'
}
}
jobName: 'job1',
},
},
});
rp.post(Parse.serverURL + '/cloud_code/jobs', options).then((res) => {
expect(res.objectId).not.toBeUndefined();
return rp.put(Parse.serverURL + '/cloud_code/jobs/' + res.objectId, Object.assign(options, {
body: {
job_schedule: {
jobName: 'job2'
}
}
}));
})
.then(() => {
return rp.get(Parse.serverURL + '/cloud_code/jobs', masterKeyOptions);
request(options)
.then(res => {
expect(res.data.objectId).not.toBeUndefined();
return request(
Object.assign(options, {
url: Parse.serverURL + '/cloud_code/jobs/' + res.data.objectId,
method: 'PUT',
body: {
job_schedule: {
jobName: 'job2',
},
},
})
);
})
.then((res) => {
expect(res.length).toBe(1);
expect(res[0].jobName).toBe('job2');
.then(() => {
return request(
Object.assign({}, masterKeyOptions, {
url: Parse.serverURL + '/cloud_code/jobs',
})
);
})
.then(res => {
expect(res.data.length).toBe(1);
expect(res.data[0].jobName).toBe('job2');
})
.then(done)
.catch(done.fail);
});
it('should fail updating a job with an invalid name', (done) => {
it('should fail updating a job with an invalid name', done => {
Parse.Cloud.job('job1', () => {});
const options = Object.assign({}, masterKeyOptions, {
method: 'POST',
url: Parse.serverURL + '/cloud_code/jobs',
body: {
job_schedule: {
jobName: 'job1'
}
}
jobName: 'job1',
},
},
});
rp.post(Parse.serverURL + '/cloud_code/jobs', options).then((res) => {
expect(res.objectId).not.toBeUndefined();
return rp.put(Parse.serverURL + '/cloud_code/jobs/' + res.objectId, Object.assign(options, {
body: {
job_schedule: {
jobName: 'job2'
}
}
}));
})
request(options)
.then(res => {
expect(res.data.objectId).not.toBeUndefined();
return request(
Object.assign(options, {
method: 'PUT',
url: Parse.serverURL + '/cloud_code/jobs/' + res.data.objectId,
body: {
job_schedule: {
jobName: 'job2',
},
},
})
);
})
.then(done.fail)
.catch(done);
.catch(() => done());
});
it('should destroy a job', (done) => {
it('should destroy a job', done => {
Parse.Cloud.job('job', () => {});
const options = Object.assign({}, masterKeyOptions, {
method: 'POST',
url: Parse.serverURL + '/cloud_code/jobs',
body: {
job_schedule: {
jobName: 'job'
}
}
jobName: 'job',
},
},
});
rp.post(Parse.serverURL + '/cloud_code/jobs', options).then((res) => {
expect(res.objectId).not.toBeUndefined();
return rp.del(Parse.serverURL + '/cloud_code/jobs/' + res.objectId, masterKeyOptions);
})
.then(() => {
return rp.get(Parse.serverURL + '/cloud_code/jobs', masterKeyOptions);
request(options)
.then(res => {
expect(res.data.objectId).not.toBeUndefined();
return request(
Object.assign(
{
method: 'DELETE',
url: Parse.serverURL + '/cloud_code/jobs/' + res.data.objectId,
},
masterKeyOptions
)
);
})
.then((res) => {
expect(res.length).toBe(0);
.then(() => {
return request(
Object.assign(
{
url: Parse.serverURL + '/cloud_code/jobs',
},
masterKeyOptions
)
);
})
.then(res => {
expect(res.data.length).toBe(0);
})
.then(done)
.catch(done.fail);
});
it('should properly return job data', (done) => {
it('should properly return job data', done => {
Parse.Cloud.job('job1', () => {});
Parse.Cloud.job('job2', () => {});
const options = Object.assign({}, masterKeyOptions, {
method: 'POST',
url: Parse.serverURL + '/cloud_code/jobs',
body: {
job_schedule: {
jobName: 'job1'
}
}
jobName: 'job1',
},
},
});
rp.post(Parse.serverURL + '/cloud_code/jobs', options).then((res) => {
expect(res.objectId).not.toBeUndefined();
})
.then(() => {
return rp.get(Parse.serverURL + '/cloud_code/jobs/data', masterKeyOptions);
request(options)
.then(response => {
const res = response.data;
expect(res.objectId).not.toBeUndefined();
})
.then((res) => {
.then(() => {
return request(
Object.assign(
{ url: Parse.serverURL + '/cloud_code/jobs/data' },
masterKeyOptions
)
);
})
.then(response => {
const res = response.data;
expect(res.in_use).toEqual(['job1']);
expect(res.jobs).toContain('job1');
expect(res.jobs).toContain('job2');
expect(res.jobs.length).toBe(2);
})
.then(done)
.catch(done.fail);
.catch(e => done.fail(e.data));
});
});
+62 -30
View File
@@ -1,63 +1,95 @@
var logging = require('../src/Adapters/Logger/WinstonLogger');
var winston = require('winston');
const logging = require('../lib/Adapters/Logger/WinstonLogger');
const Transport = require('winston-transport');
class TestTransport extends winston.Transport {
log(level, msg, meta, callback) {
class TestTransport extends Transport {
log(info, callback) {
callback(null, true);
}
}
describe('Logger', () => {
describe('WinstonLogger', () => {
it('should add transport', () => {
const testTransport = new (TestTransport)({
name: 'test'
});
const testTransport = new TestTransport();
spyOn(testTransport, 'log');
logging.addTransport(testTransport);
expect(Object.keys(logging.logger.transports).length).toBe(4);
expect(logging.logger.transports.length).toBe(4);
logging.logger.info('hi');
expect(testTransport.log).toHaveBeenCalled();
logging.logger.error('error');
expect(testTransport.log).toHaveBeenCalled();
logging.removeTransport(testTransport);
expect(Object.keys(logging.logger.transports).length).toBe(3);
expect(logging.logger.transports.length).toBe(3);
});
it('should have files transports', (done) => {
it('should have files transports', done => {
reconfigureServer().then(() => {
const transports = logging.logger.transports;
const transportKeys = Object.keys(transports);
expect(transportKeys.length).toBe(3);
expect(transports.length).toBe(3);
done();
});
});
it('should disable files logs', (done) => {
it('should disable files logs', done => {
reconfigureServer({
logsFolder: null
logsFolder: null,
}).then(() => {
const transports = logging.logger.transports;
const transportKeys = Object.keys(transports);
expect(transportKeys.length).toBe(1);
expect(transports.length).toBe(1);
done();
});
});
it('should enable JSON logs', (done) => {
it('should have a timestamp', done => {
logging.logger.info('hi');
logging.logger.query({ limit: 1 }, (err, results) => {
if (err) {
done.fail(err);
}
expect(results['parse-server'][0].timestamp).toBeDefined();
done();
});
});
it('console should not be json', done => {
// Force console transport
reconfigureServer({
logsFolder: null,
silent: false,
})
.then(() => {
spyOn(process.stdout, 'write');
logging.logger.info('hi', { key: 'value' });
expect(process.stdout.write).toHaveBeenCalled();
const firstLog = process.stdout.write.calls.first().args[0];
expect(firstLog).toEqual('info: hi {"key":"value"}' + '\n');
return reconfigureServer();
})
.then(() => {
done();
});
});
it('should enable JSON logs', done => {
// Force console transport
reconfigureServer({
logsFolder: null,
jsonLogs: true,
silent: false
}).then(() => {
spyOn(process.stdout, 'write');
logging.logger.info('hi', {key: 'value'});
expect(process.stdout.write).toHaveBeenCalled();
var firstLog = process.stdout.write.calls.first().args[0];
expect(firstLog).toEqual(JSON.stringify({key: 'value', level: 'info', message: 'hi' }) + '\n');
return reconfigureServer({
jsonLogs: false
silent: false,
})
.then(() => {
spyOn(process.stdout, 'write');
logging.logger.info('hi', { key: 'value' });
expect(process.stdout.write).toHaveBeenCalled();
const firstLog = process.stdout.write.calls.first().args[0];
expect(firstLog).toEqual(
JSON.stringify({ key: 'value', level: 'info', message: 'hi' }) + '\n'
);
return reconfigureServer({
jsonLogs: false,
});
})
.then(() => {
done();
});
}).then(() => {
done();
});
});
});
+116 -40
View File
@@ -1,35 +1,42 @@
var LoggerController = require('../src/Controllers/LoggerController').LoggerController;
var WinstonLoggerAdapter = require('../src/Adapters/Logger/WinstonLoggerAdapter').WinstonLoggerAdapter;
const LoggerController = require('../lib/Controllers/LoggerController')
.LoggerController;
const WinstonLoggerAdapter = require('../lib/Adapters/Logger/WinstonLoggerAdapter')
.WinstonLoggerAdapter;
describe('LoggerController', () => {
it('can check process a query without throwing', (done) => {
it('can process an empty query without throwing', done => {
// Make mock request
var query = {};
const query = {};
var loggerController = new LoggerController(new WinstonLoggerAdapter());
const loggerController = new LoggerController(new WinstonLoggerAdapter());
expect(() => {
loggerController.getLogs(query).then(function(res) {
expect(res.length).not.toBe(0);
done();
}).catch((err) => {
jfail(err);
done();
})
loggerController
.getLogs(query)
.then(function(res) {
expect(res.length).not.toBe(0);
done();
})
.catch(err => {
jfail(err);
done();
});
}).not.toThrow();
});
it('properly validates dateTimes', (done) => {
it('properly validates dateTimes', done => {
expect(LoggerController.validDateTime()).toBe(null);
expect(LoggerController.validDateTime("String")).toBe(null);
expect(LoggerController.validDateTime('String')).toBe(null);
expect(LoggerController.validDateTime(123456).getTime()).toBe(123456);
expect(LoggerController.validDateTime("2016-01-01Z00:00:00").getTime()).toBe(1451606400000);
expect(
LoggerController.validDateTime('2016-01-01Z00:00:00').getTime()
).toBe(1451606400000);
done();
});
it('can set the proper default values', (done) => {
it('can set the proper default values', done => {
// Make mock request
var result = LoggerController.parseOptions();
const result = LoggerController.parseOptions();
expect(result.size).toEqual(10);
expect(result.order).toEqual('desc');
expect(result.level).toEqual('info');
@@ -37,17 +44,17 @@ describe('LoggerController', () => {
done();
});
it('can process a query without throwing', (done) => {
it('can parse an ascending query without throwing', done => {
// Make mock request
var query = {
from: "2016-01-01Z00:00:00",
until: "2016-01-01Z00:00:00",
const query = {
from: '2016-01-01Z00:00:00',
until: '2016-01-01Z00:00:00',
size: 5,
order: 'asc',
level: 'error'
level: 'error',
};
var result = LoggerController.parseOptions(query);
const result = LoggerController.parseOptions(query);
expect(result.from.getTime()).toEqual(1451606400000);
expect(result.until.getTime()).toEqual(1451606400000);
@@ -58,34 +65,103 @@ describe('LoggerController', () => {
done();
});
it('can check process a query without throwing', (done) => {
// Make mock request
var query = {
from: "2016-01-01",
until: "2016-01-30",
it('can process an ascending query without throwing', done => {
const query = {
size: 5,
order: 'desc',
level: 'error'
order: 'asc',
level: 'error',
};
var loggerController = new LoggerController(new WinstonLoggerAdapter());
const loggerController = new LoggerController(new WinstonLoggerAdapter());
expect(() => {
loggerController.getLogs(query).then(function(res) {
expect(res.length).toBe(0);
done();
}).catch((err) => {
jfail(err);
fail("should not fail");
done();
})
loggerController
.getLogs(query)
.then(function(res) {
expect(res.length).not.toBe(0);
done();
})
.catch(err => {
jfail(err);
fail('should not fail');
done();
});
}).not.toThrow();
});
it('should throw without an adapter', (done) => {
it('can parse a descending query without throwing', done => {
// Make mock request
const query = {
from: '2016-01-01Z00:00:00',
until: '2016-01-01Z00:00:00',
size: 5,
order: 'desc',
level: 'error',
};
const result = LoggerController.parseOptions(query);
expect(result.from.getTime()).toEqual(1451606400000);
expect(result.until.getTime()).toEqual(1451606400000);
expect(result.size).toEqual(5);
expect(result.order).toEqual('desc');
expect(result.level).toEqual('error');
done();
});
it('can process a descending query without throwing', done => {
const query = {
size: 5,
order: 'desc',
level: 'error',
};
const loggerController = new LoggerController(new WinstonLoggerAdapter());
expect(() => {
loggerController
.getLogs(query)
.then(function(res) {
expect(res.length).not.toBe(0);
done();
})
.catch(err => {
jfail(err);
fail('should not fail');
done();
});
}).not.toThrow();
});
it('should throw without an adapter', done => {
expect(() => {
new LoggerController();
}).toThrow();
done();
});
it('should replace implementations with verbose', done => {
const adapter = new WinstonLoggerAdapter();
const logger = new LoggerController(adapter, null, { verbose: true });
spyOn(adapter, 'log');
logger.silly('yo!');
expect(adapter.log).not.toHaveBeenCalled();
done();
});
it('should replace implementations with logLevel', done => {
const adapter = new WinstonLoggerAdapter();
const logger = new LoggerController(adapter, null, { logLevel: 'error' });
spyOn(adapter, 'log');
logger.warn('yo!');
logger.info('yo!');
logger.debug('yo!');
logger.verbose('yo!');
logger.silly('yo!');
expect(adapter.log).not.toHaveBeenCalled();
logger.error('error');
expect(adapter.log).toHaveBeenCalled();
done();
});
});
+134 -22
View File
@@ -1,26 +1,28 @@
'use strict';
const request = require('request');
var LogsRouter = require('../src/Routers/LogsRouter').LogsRouter;
var LoggerController = require('../src/Controllers/LoggerController').LoggerController;
var WinstonLoggerAdapter = require('../src/Adapters/Logger/WinstonLoggerAdapter').WinstonLoggerAdapter;
const request = require('../lib/request');
const LogsRouter = require('../lib/Routers/LogsRouter').LogsRouter;
const LoggerController = require('../lib/Controllers/LoggerController')
.LoggerController;
const WinstonLoggerAdapter = require('../lib/Adapters/Logger/WinstonLoggerAdapter')
.WinstonLoggerAdapter;
const loggerController = new LoggerController(new WinstonLoggerAdapter());
describe('LogsRouter', () => {
it('can check valid master key of request', (done) => {
it('can check valid master key of request', done => {
// Make mock request
var request = {
const request = {
auth: {
isMaster: true
isMaster: true,
},
query: {},
config: {
loggerController: loggerController
}
loggerController: loggerController,
},
};
var router = new LogsRouter();
const router = new LogsRouter();
expect(() => {
router.validateRequest(request);
@@ -28,19 +30,19 @@ describe('LogsRouter', () => {
done();
});
it('can check invalid construction of controller', (done) => {
it('can check invalid construction of controller', done => {
// Make mock request
var request = {
const request = {
auth: {
isMaster: true
isMaster: true,
},
query: {},
config: {
loggerController: undefined // missing controller
}
loggerController: undefined, // missing controller
},
};
var router = new LogsRouter();
const router = new LogsRouter();
expect(() => {
router.validateRequest(request);
@@ -49,17 +51,127 @@ describe('LogsRouter', () => {
});
it('can check invalid master key of request', done => {
request.get({
request({
url: 'http://localhost:8378/1/scriptlog',
json: true,
headers: {
'X-Parse-Application-Id': 'test',
'X-Parse-REST-API-Key': 'rest'
}
}, (error, response, body) => {
expect(response.statusCode).toEqual(403);
'X-Parse-REST-API-Key': 'rest',
},
}).then(fail, response => {
const body = response.data;
expect(response.status).toEqual(403);
expect(body.error).toEqual('unauthorized: master key is required');
done();
});
});
const headers = {
'X-Parse-Application-Id': 'test',
'X-Parse-REST-API-Key': 'rest',
'X-Parse-Master-Key': 'test',
};
/**
* Verifies simple passwords in GET login requests with special characters are scrubbed from the verbose log
*/
it('does scrub simple passwords on GET login', done => {
reconfigureServer({
verbose: true,
}).then(function() {
request({
headers: headers,
url:
'http://localhost:8378/1/login?username=test&password=simplepass.com',
})
.catch(() => {})
.then(() => {
request({
url: 'http://localhost:8378/1/scriptlog?size=4&level=verbose',
headers: headers,
}).then(response => {
const body = response.data;
expect(response.status).toEqual(200);
// 4th entry is our actual GET request
expect(body[2].url).toEqual(
'/1/login?username=test&password=********'
);
expect(body[2].message).toEqual(
'REQUEST for [GET] /1/login?username=test&password=********: {}'
);
done();
});
});
});
});
/**
* Verifies complex passwords in GET login requests with special characters are scrubbed from the verbose log
*/
it('does scrub complex passwords on GET login', done => {
reconfigureServer({
verbose: true,
})
.then(function() {
return request({
headers: headers,
// using urlencoded password, 'simple @,/?:&=+$#pass.com'
url:
'http://localhost:8378/1/login?username=test&password=simple%20%40%2C%2F%3F%3A%26%3D%2B%24%23pass.com',
})
.catch(() => {})
.then(() => {
return request({
url: 'http://localhost:8378/1/scriptlog?size=4&level=verbose',
headers: headers,
}).then(response => {
const body = response.data;
expect(response.status).toEqual(200);
// 4th entry is our actual GET request
expect(body[2].url).toEqual(
'/1/login?username=test&password=********'
);
expect(body[2].message).toEqual(
'REQUEST for [GET] /1/login?username=test&password=********: {}'
);
done();
});
});
})
.catch(done.fail);
});
/**
* Verifies fields in POST login requests are NOT present in the verbose log
*/
it('does not have password field in POST login', done => {
reconfigureServer({
verbose: true,
}).then(function() {
request({
method: 'POST',
headers: headers,
url: 'http://localhost:8378/1/login',
body: {
username: 'test',
password: 'simplepass.com',
},
})
.catch(() => {})
.then(() => {
request({
url: 'http://localhost:8378/1/scriptlog?size=4&level=verbose',
headers: headers,
}).then(response => {
const body = response.data;
expect(response.status).toEqual(200);
// 4th entry is our actual GET request
expect(body[2].url).toEqual('/1/login');
expect(body[2].message).toEqual(
'REQUEST for [POST] /1/login: {\n "username": "test",\n "password": "********"\n}'
);
done();
});
});
});
});
});
+197 -26
View File
@@ -1,21 +1,20 @@
var middlewares = require('../src/middlewares');
var AppCache = require('../src/cache').AppCache;
const middlewares = require('../lib/middlewares');
const AppCache = require('../lib/cache').AppCache;
describe('middlewares', () => {
var fakeReq, fakeRes;
let fakeReq, fakeRes;
beforeEach(() => {
fakeReq = {
originalUrl: 'http://example.com/parse/',
url: 'http://example.com/',
body: {
_ApplicationId: 'FakeAppId'
_ApplicationId: 'FakeAppId',
},
headers: {},
get: (key) => {
return fakeReq.headers[key.toLowerCase()]
}
get: key => {
return fakeReq.headers[key.toLowerCase()];
},
};
fakeRes = jasmine.createSpyObj('fakeRes', ['end', 'status']);
AppCache.put(fakeReq.body._ApplicationId, {});
@@ -25,21 +24,21 @@ describe('middlewares', () => {
AppCache.del(fakeReq.body._ApplicationId);
});
it('should use _ContentType if provided', (done) => {
it('should use _ContentType if provided', done => {
expect(fakeReq.headers['content-type']).toEqual(undefined);
var contentType = 'image/jpeg';
const contentType = 'image/jpeg';
fakeReq.body._ContentType = contentType;
middlewares.handleParseHeaders(fakeReq, fakeRes, () => {
expect(fakeReq.headers['content-type']).toEqual(contentType);
expect(fakeReq.body._ContentType).toEqual(undefined);
done()
done();
});
});
it('should give invalid response when keys are configured but no key supplied', () => {
AppCache.put(fakeReq.body._ApplicationId, {
masterKey: 'masterKey',
restAPIKey: 'restAPIKey'
restAPIKey: 'restAPIKey',
});
middlewares.handleParseHeaders(fakeReq, fakeRes);
expect(fakeRes.status).toHaveBeenCalledWith(403);
@@ -48,7 +47,7 @@ describe('middlewares', () => {
it('should give invalid response when keys are configured but supplied key is incorrect', () => {
AppCache.put(fakeReq.body._ApplicationId, {
masterKey: 'masterKey',
restAPIKey: 'restAPIKey'
restAPIKey: 'restAPIKey',
});
fakeReq.headers['x-parse-rest-api-key'] = 'wrongKey';
middlewares.handleParseHeaders(fakeReq, fakeRes);
@@ -58,19 +57,18 @@ describe('middlewares', () => {
it('should give invalid response when keys are configured but different key is supplied', () => {
AppCache.put(fakeReq.body._ApplicationId, {
masterKey: 'masterKey',
restAPIKey: 'restAPIKey'
restAPIKey: 'restAPIKey',
});
fakeReq.headers['x-parse-client-key'] = 'clientKey';
middlewares.handleParseHeaders(fakeReq, fakeRes);
expect(fakeRes.status).toHaveBeenCalledWith(403);
});
it('should succeed when any one of the configured keys supplied', (done) => {
it('should succeed when any one of the configured keys supplied', done => {
AppCache.put(fakeReq.body._ApplicationId, {
clientKey: 'clientKey',
masterKey: 'masterKey',
restAPIKey: 'restAPIKey'
restAPIKey: 'restAPIKey',
});
fakeReq.headers['x-parse-rest-api-key'] = 'restAPIKey';
middlewares.handleParseHeaders(fakeReq, fakeRes, () => {
@@ -79,11 +77,11 @@ describe('middlewares', () => {
});
});
it('should succeed when client key supplied but empty', (done) => {
it('should succeed when client key supplied but empty', done => {
AppCache.put(fakeReq.body._ApplicationId, {
clientKey: '',
masterKey: 'masterKey',
restAPIKey: 'restAPIKey'
restAPIKey: 'restAPIKey',
});
fakeReq.headers['x-parse-client-key'] = '';
middlewares.handleParseHeaders(fakeReq, fakeRes, () => {
@@ -92,9 +90,9 @@ describe('middlewares', () => {
});
});
it('should succeed when no keys are configured and none supplied', (done) => {
it('should succeed when no keys are configured and none supplied', done => {
AppCache.put(fakeReq.body._ApplicationId, {
masterKey: 'masterKey'
masterKey: 'masterKey',
});
middlewares.handleParseHeaders(fakeReq, fakeRes, () => {
expect(fakeRes.status).not.toHaveBeenCalled();
@@ -107,25 +105,27 @@ describe('middlewares', () => {
installationId: '_InstallationId',
sessionToken: '_SessionToken',
masterKey: '_MasterKey',
javascriptKey: '_JavaScriptKey'
javascriptKey: '_JavaScriptKey',
};
const BodyKeys = Object.keys(BodyParams);
BodyKeys.forEach((infoKey) => {
BodyKeys.forEach(infoKey => {
const bodyKey = BodyParams[infoKey];
const keyValue = 'Fake' + bodyKey;
// javascriptKey is the only one that gets defaulted,
const otherKeys = BodyKeys.filter((otherKey) => otherKey !== infoKey && otherKey !== 'javascriptKey');
const otherKeys = BodyKeys.filter(
otherKey => otherKey !== infoKey && otherKey !== 'javascriptKey'
);
it(`it should pull ${bodyKey} into req.info`, (done) => {
it(`it should pull ${bodyKey} into req.info`, done => {
fakeReq.body[bodyKey] = keyValue;
middlewares.handleParseHeaders(fakeReq, fakeRes, () => {
expect(fakeReq.body[bodyKey]).toEqual(undefined);
expect(fakeReq.info[infoKey]).toEqual(keyValue);
otherKeys.forEach((otherKey) => {
otherKeys.forEach(otherKey => {
expect(fakeReq.info[otherKey]).toEqual(undefined);
});
@@ -133,4 +133,175 @@ describe('middlewares', () => {
});
});
});
it('should not succeed if the ip does not belong to masterKeyIps list', () => {
AppCache.put(fakeReq.body._ApplicationId, {
masterKey: 'masterKey',
masterKeyIps: ['ip1', 'ip2'],
});
fakeReq.ip = 'ip3';
fakeReq.headers['x-parse-master-key'] = 'masterKey';
middlewares.handleParseHeaders(fakeReq, fakeRes);
expect(fakeRes.status).toHaveBeenCalledWith(403);
});
it('should succeed if the ip does belong to masterKeyIps list', done => {
AppCache.put(fakeReq.body._ApplicationId, {
masterKey: 'masterKey',
masterKeyIps: ['ip1', 'ip2'],
});
fakeReq.ip = 'ip1';
fakeReq.headers['x-parse-master-key'] = 'masterKey';
middlewares.handleParseHeaders(fakeReq, fakeRes, () => {
expect(fakeRes.status).not.toHaveBeenCalled();
done();
});
});
it('should not succeed if the connection.remoteAddress does not belong to masterKeyIps list', () => {
AppCache.put(fakeReq.body._ApplicationId, {
masterKey: 'masterKey',
masterKeyIps: ['ip1', 'ip2'],
});
fakeReq.connection = { remoteAddress: 'ip3' };
fakeReq.headers['x-parse-master-key'] = 'masterKey';
middlewares.handleParseHeaders(fakeReq, fakeRes);
expect(fakeRes.status).toHaveBeenCalledWith(403);
});
it('should succeed if the connection.remoteAddress does belong to masterKeyIps list', done => {
AppCache.put(fakeReq.body._ApplicationId, {
masterKey: 'masterKey',
masterKeyIps: ['ip1', 'ip2'],
});
fakeReq.connection = { remoteAddress: 'ip1' };
fakeReq.headers['x-parse-master-key'] = 'masterKey';
middlewares.handleParseHeaders(fakeReq, fakeRes, () => {
expect(fakeRes.status).not.toHaveBeenCalled();
done();
});
});
it('should not succeed if the socket.remoteAddress does not belong to masterKeyIps list', () => {
AppCache.put(fakeReq.body._ApplicationId, {
masterKey: 'masterKey',
masterKeyIps: ['ip1', 'ip2'],
});
fakeReq.socket = { remoteAddress: 'ip3' };
fakeReq.headers['x-parse-master-key'] = 'masterKey';
middlewares.handleParseHeaders(fakeReq, fakeRes);
expect(fakeRes.status).toHaveBeenCalledWith(403);
});
it('should succeed if the socket.remoteAddress does belong to masterKeyIps list', done => {
AppCache.put(fakeReq.body._ApplicationId, {
masterKey: 'masterKey',
masterKeyIps: ['ip1', 'ip2'],
});
fakeReq.socket = { remoteAddress: 'ip1' };
fakeReq.headers['x-parse-master-key'] = 'masterKey';
middlewares.handleParseHeaders(fakeReq, fakeRes, () => {
expect(fakeRes.status).not.toHaveBeenCalled();
done();
});
});
it('should not succeed if the connection.socket.remoteAddress does not belong to masterKeyIps list', () => {
AppCache.put(fakeReq.body._ApplicationId, {
masterKey: 'masterKey',
masterKeyIps: ['ip1', 'ip2'],
});
fakeReq.connection = { socket: { remoteAddress: 'ip3' } };
fakeReq.headers['x-parse-master-key'] = 'masterKey';
middlewares.handleParseHeaders(fakeReq, fakeRes);
expect(fakeRes.status).toHaveBeenCalledWith(403);
});
it('should succeed if the connection.socket.remoteAddress does belong to masterKeyIps list', done => {
AppCache.put(fakeReq.body._ApplicationId, {
masterKey: 'masterKey',
masterKeyIps: ['ip1', 'ip2'],
});
fakeReq.connection = { socket: { remoteAddress: 'ip1' } };
fakeReq.headers['x-parse-master-key'] = 'masterKey';
middlewares.handleParseHeaders(fakeReq, fakeRes, () => {
expect(fakeRes.status).not.toHaveBeenCalled();
done();
});
});
it('should allow any ip to use masterKey if masterKeyIps is empty', done => {
AppCache.put(fakeReq.body._ApplicationId, {
masterKey: 'masterKey',
masterKeyIps: [],
});
fakeReq.ip = 'ip1';
fakeReq.headers['x-parse-master-key'] = 'masterKey';
middlewares.handleParseHeaders(fakeReq, fakeRes, () => {
expect(fakeRes.status).not.toHaveBeenCalled();
done();
});
});
it('should succeed if xff header does belong to masterKeyIps', done => {
AppCache.put(fakeReq.body._ApplicationId, {
masterKey: 'masterKey',
masterKeyIps: ['ip1'],
});
fakeReq.headers['x-parse-master-key'] = 'masterKey';
fakeReq.headers['x-forwarded-for'] = 'ip1, ip2, ip3';
middlewares.handleParseHeaders(fakeReq, fakeRes, () => {
expect(fakeRes.status).not.toHaveBeenCalled();
done();
});
});
it('should succeed if xff header with one ip does belong to masterKeyIps', done => {
AppCache.put(fakeReq.body._ApplicationId, {
masterKey: 'masterKey',
masterKeyIps: ['ip1'],
});
fakeReq.headers['x-parse-master-key'] = 'masterKey';
fakeReq.headers['x-forwarded-for'] = 'ip1';
middlewares.handleParseHeaders(fakeReq, fakeRes, () => {
expect(fakeRes.status).not.toHaveBeenCalled();
done();
});
});
it('should not succeed if xff header does not belong to masterKeyIps', () => {
AppCache.put(fakeReq.body._ApplicationId, {
masterKey: 'masterKey',
masterKeyIps: ['ip4'],
});
fakeReq.headers['x-parse-master-key'] = 'masterKey';
fakeReq.headers['x-forwarded-for'] = 'ip1, ip2, ip3';
middlewares.handleParseHeaders(fakeReq, fakeRes);
expect(fakeRes.status).toHaveBeenCalledWith(403);
});
it('should not succeed if xff header is empty and masterKeyIps is set', () => {
AppCache.put(fakeReq.body._ApplicationId, {
masterKey: 'masterKey',
masterKeyIps: ['ip1'],
});
fakeReq.headers['x-parse-master-key'] = 'masterKey';
fakeReq.headers['x-forwarded-for'] = '';
middlewares.handleParseHeaders(fakeReq, fakeRes);
expect(fakeRes.status).toHaveBeenCalledWith(403);
});
it('should properly expose the headers', () => {
const headers = {};
const res = {
header: (key, value) => {
headers[key] = value;
},
};
middlewares.allowCrossDomain({}, res, () => {});
expect(Object.keys(headers).length).toBe(4);
expect(headers['Access-Control-Expose-Headers']).toBe(
'X-Parse-Job-Status-Id, X-Parse-Push-Status-Id'
);
});
});
+1 -1
View File
@@ -1,5 +1,5 @@
module.exports = function(options) {
return {
options: options
options: options,
};
};
+2 -2
View File
@@ -1,5 +1,5 @@
module.exports = {
sendVerificationEmail: () => Promise.resolve(),
sendPasswordResetEmail: () => Promise.resolve(),
sendMail: () => Promise.resolve()
}
sendMail: () => Promise.resolve(),
};
+5 -5
View File
@@ -1,21 +1,21 @@
module.exports = options => {
if (!options) {
throw "Options were not provided"
throw 'Options were not provided';
}
const adapter = {
sendVerificationEmail: () => Promise.resolve(),
sendPasswordResetEmail: () => Promise.resolve(),
sendMail: () => Promise.resolve()
sendMail: () => Promise.resolve(),
};
if (options.sendMail) {
adapter.sendMail = options.sendMail
adapter.sendMail = options.sendMail;
}
if (options.sendPasswordResetEmail) {
adapter.sendPasswordResetEmail = options.sendPasswordResetEmail
adapter.sendPasswordResetEmail = options.sendPasswordResetEmail;
}
if (options.sendVerificationEmail) {
adapter.sendVerificationEmail = options.sendVerificationEmail;
}
return adapter;
}
};
+1 -1
View File
@@ -4,6 +4,6 @@ module.exports = function(options) {
send: function() {},
getValidPushTypes: function() {
return Object.keys(options.options);
}
},
};
};
+47 -36
View File
@@ -1,43 +1,50 @@
'use strict';
const MongoSchemaCollection = require('../src/Adapters/Storage/Mongo/MongoSchemaCollection').default;
const MongoSchemaCollection = require('../lib/Adapters/Storage/Mongo/MongoSchemaCollection')
.default;
describe('MongoSchemaCollection', () => {
it('can transform legacy _client_permissions keys to parse format', done => {
expect(MongoSchemaCollection._TESTmongoSchemaToParseSchema({
"_id":"_Installation",
"_client_permissions":{
"get":true,
"find":true,
"update":true,
"create":true,
"delete":true,
},
"_metadata":{
"class_permissions":{
"get":{"*":true},
"find":{"*":true},
"update":{"*":true},
"create":{"*":true},
"delete":{"*":true},
"addField":{"*":true},
}
},
"installationId":"string",
"deviceToken":"string",
"deviceType":"string",
"channels":"array",
"user":"*_User",
"pushType":"string",
"GCMSenderId":"string",
"timeZone":"string",
"localeIdentifier":"string",
"badge":"number",
"appVersion":"string",
"appName":"string",
"appIdentifier":"string",
"parseVersion":"string",
})).toEqual({
expect(
MongoSchemaCollection._TESTmongoSchemaToParseSchema({
_id: '_Installation',
_client_permissions: {
get: true,
find: true,
update: true,
create: true,
delete: true,
},
_metadata: {
class_permissions: {
get: { '*': true },
find: { '*': true },
update: { '*': true },
create: { '*': true },
delete: { '*': true },
addField: { '*': true },
protectedFields: { '*': [] },
},
indexes: {
name1: { deviceToken: 1 },
},
},
installationId: 'string',
deviceToken: 'string',
deviceType: 'string',
channels: 'array',
user: '*_User',
pushType: 'string',
GCMSenderId: 'string',
timeZone: 'string',
localeIdentifier: 'string',
badge: 'number',
appVersion: 'string',
appName: 'string',
appIdentifier: 'string',
parseVersion: 'string',
})
).toEqual({
className: '_Installation',
fields: {
installationId: { type: 'String' },
@@ -66,7 +73,11 @@ describe('MongoSchemaCollection', () => {
update: { '*': true },
delete: { '*': true },
addField: { '*': true },
}
protectedFields: { '*': [] },
},
indexes: {
name1: { deviceToken: 1 },
},
});
done();
});
+99 -50
View File
@@ -1,8 +1,15 @@
'use strict';
const MongoStorageAdapter = require('../src/Adapters/Storage/Mongo/MongoStorageAdapter');
const MongoClient = require('mongodb').MongoClient;
const databaseURI = 'mongodb://localhost:27017/parseServerMongoAdapterTestDatabase';
const MongoStorageAdapter = require('../lib/Adapters/Storage/Mongo/MongoStorageAdapter')
.default;
const { MongoClient } = require('mongodb');
const databaseURI =
'mongodb://localhost:27017/parseServerMongoAdapterTestDatabase';
const fakeClient = {
s: { options: { dbName: null } },
db: () => null,
};
// These tests are specific to the mongo storage adapter + mongo storage format
// and will eventually be moved into their own repo
@@ -14,9 +21,10 @@ describe_only_db('mongo')('MongoStorageAdapter', () => {
});
it('auto-escapes symbols in auth information', () => {
spyOn(MongoClient, 'connect').and.returnValue(Promise.resolve(null));
spyOn(MongoClient, 'connect').and.returnValue(Promise.resolve(fakeClient));
new MongoStorageAdapter({
uri: 'mongodb://user!with@+ symbols:password!with@+ symbols@localhost:1234/parse'
uri:
'mongodb://user!with@+ symbols:password!with@+ symbols@localhost:1234/parse',
}).connect();
expect(MongoClient.connect).toHaveBeenCalledWith(
'mongodb://user!with%40%2B%20symbols:password!with%40%2B%20symbols@localhost:1234/parse',
@@ -25,9 +33,10 @@ describe_only_db('mongo')('MongoStorageAdapter', () => {
});
it("doesn't double escape already URI-encoded information", () => {
spyOn(MongoClient, 'connect').and.returnValue(Promise.resolve(null));
spyOn(MongoClient, 'connect').and.returnValue(Promise.resolve(fakeClient));
new MongoStorageAdapter({
uri: 'mongodb://user!with%40%2B%20symbols:password!with%40%2B%20symbols@localhost:1234/parse'
uri:
'mongodb://user!with%40%2B%20symbols:password!with%40%2B%20symbols@localhost:1234/parse',
}).connect();
expect(MongoClient.connect).toHaveBeenCalledWith(
'mongodb://user!with%40%2B%20symbols:password!with%40%2B%20symbols@localhost:1234/parse',
@@ -35,11 +44,12 @@ describe_only_db('mongo')('MongoStorageAdapter', () => {
);
});
// https://github.com/ParsePlatform/parse-server/pull/148#issuecomment-180407057
// https://github.com/parse-community/parse-server/pull/148#issuecomment-180407057
it('preserves replica sets', () => {
spyOn(MongoClient, 'connect').and.returnValue(Promise.resolve(null));
spyOn(MongoClient, 'connect').and.returnValue(Promise.resolve(fakeClient));
new MongoStorageAdapter({
uri: 'mongodb://test:testpass@ds056315-a0.mongolab.com:59325,ds059315-a1.mongolab.com:59315/testDBname?replicaSet=rs-ds059415'
uri:
'mongodb://test:testpass@ds056315-a0.mongolab.com:59325,ds059315-a1.mongolab.com:59315/testDBname?replicaSet=rs-ds059415',
}).connect();
expect(MongoClient.connect).toHaveBeenCalledWith(
'mongodb://test:testpass@ds056315-a0.mongolab.com:59325,ds059315-a1.mongolab.com:59315/testDBname?replicaSet=rs-ds059415',
@@ -49,68 +59,82 @@ describe_only_db('mongo')('MongoStorageAdapter', () => {
it('stores objectId in _id', done => {
const adapter = new MongoStorageAdapter({ uri: databaseURI });
adapter.createObject('Foo', { fields: {} }, { objectId: 'abcde' })
adapter
.createObject('Foo', { fields: {} }, { objectId: 'abcde' })
.then(() => adapter._rawFind('Foo', {}))
.then(results => {
expect(results.length).toEqual(1);
var obj = results[0];
const obj = results[0];
expect(obj._id).toEqual('abcde');
expect(obj.objectId).toBeUndefined();
done();
});
});
it('find succeeds when query is within maxTimeMS', (done) => {
it('find succeeds when query is within maxTimeMS', done => {
const maxTimeMS = 250;
const adapter = new MongoStorageAdapter({
uri: databaseURI,
mongoOptions: { maxTimeMS },
});
adapter.createObject('Foo', { fields: {} }, { objectId: 'abcde' })
.then(() => adapter._rawFind('Foo', { '$where': `sleep(${maxTimeMS / 2})` }))
adapter
.createObject('Foo', { fields: {} }, { objectId: 'abcde' })
.then(() =>
adapter._rawFind('Foo', { $where: `sleep(${maxTimeMS / 2})` })
)
.then(
() => done(),
(err) => {
err => {
done.fail(`maxTimeMS should not affect fast queries ${err}`);
}
);
})
});
it('find fails when query exceeds maxTimeMS', (done) => {
it('find fails when query exceeds maxTimeMS', done => {
const maxTimeMS = 250;
const adapter = new MongoStorageAdapter({
uri: databaseURI,
mongoOptions: { maxTimeMS },
});
adapter.createObject('Foo', { fields: {} }, { objectId: 'abcde' })
.then(() => adapter._rawFind('Foo', { '$where': `sleep(${maxTimeMS * 2})` }))
adapter
.createObject('Foo', { fields: {} }, { objectId: 'abcde' })
.then(() =>
adapter._rawFind('Foo', { $where: `sleep(${maxTimeMS * 2})` })
)
.then(
() => {
done.fail('Find succeeded despite taking too long!');
},
(err) => {
err => {
expect(err.name).toEqual('MongoError');
expect(err.code).toEqual(50);
expect(err.message).toEqual('operation exceeded time limit');
expect(err.message).toMatch('operation exceeded time limit');
done();
}
);
});
it('stores pointers with a _p_ prefix', (done) => {
it('stores pointers with a _p_ prefix', done => {
const obj = {
objectId: 'bar',
aPointer: {
__type: 'Pointer',
className: 'JustThePointer',
objectId: 'qwerty'
}
objectId: 'qwerty',
},
};
const adapter = new MongoStorageAdapter({ uri: databaseURI });
adapter.createObject('APointerDarkly', { fields: {
objectId: { type: 'String' },
aPointer: { type: 'Pointer', targetClass: 'JustThePointer' },
}}, obj)
adapter
.createObject(
'APointerDarkly',
{
fields: {
objectId: { type: 'String' },
aPointer: { type: 'Pointer', targetClass: 'JustThePointer' },
},
},
obj
)
.then(() => adapter._rawFind('APointerDarkly', {}))
.then(results => {
expect(results.length).toEqual(1);
@@ -125,9 +149,10 @@ describe_only_db('mongo')('MongoStorageAdapter', () => {
it('handles object and subdocument', done => {
const adapter = new MongoStorageAdapter({ uri: databaseURI });
const schema = { fields : { subdoc: { type: 'Object' } } };
const obj = { subdoc: {foo: 'bar', wu: 'tan'} };
adapter.createObject('MyClass', schema, obj)
const schema = { fields: { subdoc: { type: 'Object' } } };
const obj = { subdoc: { foo: 'bar', wu: 'tan' } };
adapter
.createObject('MyClass', schema, obj)
.then(() => adapter._rawFind('MyClass', {}))
.then(results => {
expect(results.length).toEqual(1);
@@ -149,22 +174,25 @@ describe_only_db('mongo')('MongoStorageAdapter', () => {
});
});
it('handles creating an array, object, date', (done) => {
it('handles creating an array, object, date', done => {
const adapter = new MongoStorageAdapter({ uri: databaseURI });
const obj = {
array: [1, 2, 3],
object: {foo: 'bar'},
object: { foo: 'bar' },
date: {
__type: 'Date',
iso: '2016-05-26T20:55:01.154Z',
},
};
const schema = { fields: {
array: { type: 'Array' },
object: { type: 'Object' },
date: { type: 'Date' },
} };
adapter.createObject('MyClass', schema, obj)
const schema = {
fields: {
array: { type: 'Array' },
object: { type: 'Object' },
date: { type: 'Date' },
},
};
adapter
.createObject('MyClass', schema, obj)
.then(() => adapter._rawFind('MyClass', {}))
.then(results => {
expect(results.length).toEqual(1);
@@ -190,30 +218,32 @@ describe_only_db('mongo')('MongoStorageAdapter', () => {
});
});
it("handles updating a single object with array, object date", (done) => {
it('handles updating a single object with array, object date', done => {
const adapter = new MongoStorageAdapter({ uri: databaseURI });
const schema = { fields: {
array: { type: 'Array' },
object: { type: 'Object' },
date: { type: 'Date' },
} };
const schema = {
fields: {
array: { type: 'Array' },
object: { type: 'Object' },
date: { type: 'Date' },
},
};
adapter.createObject('MyClass', schema, {})
adapter
.createObject('MyClass', schema, {})
.then(() => adapter._rawFind('MyClass', {}))
.then(results => {
expect(results.length).toEqual(1);
const update = {
array: [1, 2, 3],
object: {foo: 'bar'},
object: { foo: 'bar' },
date: {
__type: 'Date',
iso: '2016-05-26T20:55:01.154Z',
},
};
const query = {};
return adapter.findOneAndUpdate('MyClass', schema, query, update)
return adapter.findOneAndUpdate('MyClass', schema, query, update);
})
.then(results => {
const mob = results;
@@ -237,4 +267,23 @@ describe_only_db('mongo')('MongoStorageAdapter', () => {
done();
});
});
it('handleShutdown, close connection', done => {
const adapter = new MongoStorageAdapter({ uri: databaseURI });
const schema = {
fields: {
array: { type: 'Array' },
object: { type: 'Object' },
date: { type: 'Date' },
},
};
adapter.createObject('MyClass', schema, {}).then(() => {
expect(adapter.database.serverConfig.isConnected()).toEqual(true);
adapter.handleShutdown();
expect(adapter.database.serverConfig.isConnected()).toEqual(false);
done();
});
});
});
+431 -145
View File
@@ -1,233 +1,319 @@
// These tests are unit tests designed to only test transform.js.
"use strict";
'use strict';
const transform = require('../src/Adapters/Storage/Mongo/MongoTransform');
const transform = require('../lib/Adapters/Storage/Mongo/MongoTransform');
const dd = require('deep-diff');
const mongodb = require('mongodb');
describe('parseObjectToMongoObjectForCreate', () => {
it('a basic number', (done) => {
var input = {five: 5};
var output = transform.parseObjectToMongoObjectForCreate(null, input, {
fields: {five: {type: 'Number'}}
it('a basic number', done => {
const input = { five: 5 };
const output = transform.parseObjectToMongoObjectForCreate(null, input, {
fields: { five: { type: 'Number' } },
});
jequal(input, output);
done();
});
it('an object with null values', (done) => {
var input = {objectWithNullValues: {isNull: null, notNull: 3}};
var output = transform.parseObjectToMongoObjectForCreate(null, input, {
fields: {objectWithNullValues: {type: 'object'}}
it('an object with null values', done => {
const input = { objectWithNullValues: { isNull: null, notNull: 3 } };
const output = transform.parseObjectToMongoObjectForCreate(null, input, {
fields: { objectWithNullValues: { type: 'object' } },
});
jequal(input, output);
done();
});
it('built-in timestamps', (done) => {
var input = {
createdAt: "2015-10-06T21:24:50.332Z",
updatedAt: "2015-10-06T21:24:50.332Z"
it('built-in timestamps with date', done => {
const input = {
createdAt: '2015-10-06T21:24:50.332Z',
updatedAt: '2015-10-06T21:24:50.332Z',
};
var output = transform.parseObjectToMongoObjectForCreate(null, input, { fields: {} });
const output = transform.parseObjectToMongoObjectForCreate(null, input, {
fields: {},
});
expect(output._created_at instanceof Date).toBe(true);
expect(output._updated_at instanceof Date).toBe(true);
done();
});
it('array of pointers', (done) => {
var pointer = {
it('array of pointers', done => {
const pointer = {
__type: 'Pointer',
objectId: 'myId',
className: 'Blah',
};
var out = transform.parseObjectToMongoObjectForCreate(null, {pointers: [pointer]},{
fields: {pointers: {type: 'Array'}}
});
const out = transform.parseObjectToMongoObjectForCreate(
null,
{ pointers: [pointer] },
{
fields: { pointers: { type: 'Array' } },
}
);
jequal([pointer], out.pointers);
done();
});
//TODO: object creation requests shouldn't be seeing __op delete, it makes no sense to
//have __op delete in a new object. Figure out what this should actually be testing.
xit('a delete op', (done) => {
var input = {deleteMe: {__op: 'Delete'}};
var output = transform.parseObjectToMongoObjectForCreate(null, input, { fields: {} });
xit('a delete op', done => {
const input = { deleteMe: { __op: 'Delete' } };
const output = transform.parseObjectToMongoObjectForCreate(null, input, {
fields: {},
});
jequal(output, {});
done();
});
it('Doesnt allow ACL, as Parse Server should tranform ACL to _wperm + _rperm', done => {
var input = {ACL: {'0123': {'read': true, 'write': true}}};
expect(() => transform.parseObjectToMongoObjectForCreate(null, input, { fields: {} })).toThrow();
const input = { ACL: { '0123': { read: true, write: true } } };
expect(() =>
transform.parseObjectToMongoObjectForCreate(null, input, { fields: {} })
).toThrow();
done();
});
it('plain', (done) => {
var geoPoint = {__type: 'GeoPoint', longitude: 180, latitude: -180};
var out = transform.parseObjectToMongoObjectForCreate(null, {location: geoPoint},{
fields: {location: {type: 'GeoPoint'}}
});
expect(out.location).toEqual([180, -180]);
it('parse geopoint to mongo', done => {
const lat = -45;
const lng = 45;
const geoPoint = { __type: 'GeoPoint', latitude: lat, longitude: lng };
const out = transform.parseObjectToMongoObjectForCreate(
null,
{ location: geoPoint },
{
fields: { location: { type: 'GeoPoint' } },
}
);
expect(out.location).toEqual([lng, lat]);
done();
});
it('in array', (done) => {
var geoPoint = {__type: 'GeoPoint', longitude: 180, latitude: -180};
var out = transform.parseObjectToMongoObjectForCreate(null, {locations: [geoPoint, geoPoint]},{
fields: {locations: {type: 'Array'}}
});
it('parse polygon to mongo', done => {
const lat1 = -45;
const lng1 = 45;
const lat2 = -55;
const lng2 = 55;
const lat3 = -65;
const lng3 = 65;
const polygon = {
__type: 'Polygon',
coordinates: [[lat1, lng1], [lat2, lng2], [lat3, lng3]],
};
const out = transform.parseObjectToMongoObjectForCreate(
null,
{ location: polygon },
{
fields: { location: { type: 'Polygon' } },
}
);
expect(out.location.coordinates).toEqual([
[[lng1, lat1], [lng2, lat2], [lng3, lat3], [lng1, lat1]],
]);
done();
});
it('in array', done => {
const geoPoint = { __type: 'GeoPoint', longitude: 180, latitude: -180 };
const out = transform.parseObjectToMongoObjectForCreate(
null,
{ locations: [geoPoint, geoPoint] },
{
fields: { locations: { type: 'Array' } },
}
);
expect(out.locations).toEqual([geoPoint, geoPoint]);
done();
});
it('in sub-object', (done) => {
var geoPoint = {__type: 'GeoPoint', longitude: 180, latitude: -180};
var out = transform.parseObjectToMongoObjectForCreate(null, { locations: { start: geoPoint }},{
fields: {locations: {type: 'Object'}}
});
it('in sub-object', done => {
const geoPoint = { __type: 'GeoPoint', longitude: 180, latitude: -180 };
const out = transform.parseObjectToMongoObjectForCreate(
null,
{ locations: { start: geoPoint } },
{
fields: { locations: { type: 'Object' } },
}
);
expect(out).toEqual({ locations: { start: geoPoint } });
done();
});
it('objectId', (done) => {
var out = transform.transformWhere(null, {objectId: 'foo'});
it('objectId', done => {
const out = transform.transformWhere(null, { objectId: 'foo' });
expect(out._id).toEqual('foo');
done();
});
it('objectId in a list', (done) => {
var input = {
objectId: {'$in': ['one', 'two', 'three']},
it('objectId in a list', done => {
const input = {
objectId: { $in: ['one', 'two', 'three'] },
};
var output = transform.transformWhere(null, input);
const output = transform.transformWhere(null, input);
jequal(input.objectId, output._id);
done();
});
it('built-in timestamps', (done) => {
var input = {createdAt: new Date(), updatedAt: new Date()};
var output = transform.mongoObjectToParseObject(null, input, { fields: {} });
it('built-in timestamps', done => {
const input = { createdAt: new Date(), updatedAt: new Date() };
const output = transform.mongoObjectToParseObject(null, input, {
fields: {},
});
expect(typeof output.createdAt).toEqual('string');
expect(typeof output.updatedAt).toEqual('string');
done();
});
it('pointer', (done) => {
var input = {_p_userPointer: '_User$123'};
var output = transform.mongoObjectToParseObject(null, input, {
it('pointer', done => {
const input = { _p_userPointer: '_User$123' };
const output = transform.mongoObjectToParseObject(null, input, {
fields: { userPointer: { type: 'Pointer', targetClass: '_User' } },
});
expect(typeof output.userPointer).toEqual('object');
expect(output.userPointer).toEqual(
{__type: 'Pointer', className: '_User', objectId: '123'}
);
expect(output.userPointer).toEqual({
__type: 'Pointer',
className: '_User',
objectId: '123',
});
done();
});
it('null pointer', (done) => {
var input = {_p_userPointer: null};
var output = transform.mongoObjectToParseObject(null, input, {
it('null pointer', done => {
const input = { _p_userPointer: null };
const output = transform.mongoObjectToParseObject(null, input, {
fields: { userPointer: { type: 'Pointer', targetClass: '_User' } },
});
expect(output.userPointer).toBeUndefined();
done();
});
it('file', (done) => {
var input = {picture: 'pic.jpg'};
var output = transform.mongoObjectToParseObject(null, input, {
fields: { picture: { type: 'File' }},
it('file', done => {
const input = { picture: 'pic.jpg' };
const output = transform.mongoObjectToParseObject(null, input, {
fields: { picture: { type: 'File' } },
});
expect(typeof output.picture).toEqual('object');
expect(output.picture).toEqual({__type: 'File', name: 'pic.jpg'});
expect(output.picture).toEqual({ __type: 'File', name: 'pic.jpg' });
done();
});
it('geopoint', (done) => {
var input = {location: [180, -180]};
var output = transform.mongoObjectToParseObject(null, input, {
fields: { location: { type: 'GeoPoint' }},
it('mongo geopoint to parse', done => {
const lat = -45;
const lng = 45;
const input = { location: [lng, lat] };
const output = transform.mongoObjectToParseObject(null, input, {
fields: { location: { type: 'GeoPoint' } },
});
expect(typeof output.location).toEqual('object');
expect(output.location).toEqual(
{__type: 'GeoPoint', longitude: 180, latitude: -180}
);
expect(output.location).toEqual({
__type: 'GeoPoint',
latitude: lat,
longitude: lng,
});
done();
});
it('bytes', (done) => {
var input = {binaryData: "aGVsbG8gd29ybGQ="};
var output = transform.mongoObjectToParseObject(null, input, {
fields: { binaryData: { type: 'Bytes' }},
it('mongo polygon to parse', done => {
const lat = -45;
const lng = 45;
// Mongo stores polygon in WGS84 lng/lat
const input = {
location: { type: 'Polygon', coordinates: [[[lat, lng], [lat, lng]]] },
};
const output = transform.mongoObjectToParseObject(null, input, {
fields: { location: { type: 'Polygon' } },
});
expect(typeof output.location).toEqual('object');
expect(output.location).toEqual({
__type: 'Polygon',
coordinates: [[lng, lat], [lng, lat]],
});
done();
});
it('bytes', done => {
const input = { binaryData: 'aGVsbG8gd29ybGQ=' };
const output = transform.mongoObjectToParseObject(null, input, {
fields: { binaryData: { type: 'Bytes' } },
});
expect(typeof output.binaryData).toEqual('object');
expect(output.binaryData).toEqual(
{__type: 'Bytes', base64: "aGVsbG8gd29ybGQ="}
);
expect(output.binaryData).toEqual({
__type: 'Bytes',
base64: 'aGVsbG8gd29ybGQ=',
});
done();
});
it('nested array', (done) => {
var input = {arr: [{_testKey: 'testValue' }]};
var output = transform.mongoObjectToParseObject(null, input, {
it('nested array', done => {
const input = { arr: [{ _testKey: 'testValue' }] };
const output = transform.mongoObjectToParseObject(null, input, {
fields: { arr: { type: 'Array' } },
});
expect(Array.isArray(output.arr)).toEqual(true);
expect(output.arr).toEqual([{ _testKey: 'testValue'}]);
expect(output.arr).toEqual([{ _testKey: 'testValue' }]);
done();
});
it('untransforms objects containing nested special keys', done => {
const input = {array: [{
_id: "Test ID",
_hashed_password: "I Don't know why you would name a key this, but if you do it should work",
_tombstone: {
_updated_at: "I'm sure people will nest keys like this",
_acl: 7,
_id: { someString: "str", someNumber: 7},
regularKey: { moreContents: [1, 2, 3] },
},
regularKey: "some data",
}]}
const input = {
array: [
{
_id: 'Test ID',
_hashed_password:
"I Don't know why you would name a key this, but if you do it should work",
_tombstone: {
_updated_at: "I'm sure people will nest keys like this",
_acl: 7,
_id: { someString: 'str', someNumber: 7 },
regularKey: { moreContents: [1, 2, 3] },
},
regularKey: 'some data',
},
],
};
const output = transform.mongoObjectToParseObject(null, input, {
fields: { array: { type: 'Array' }},
fields: { array: { type: 'Array' } },
});
expect(dd(output, input)).toEqual(undefined);
done();
});
it('changes new pointer key', (done) => {
var input = {
somePointer: {__type: 'Pointer', className: 'Micro', objectId: 'oft'}
it('changes new pointer key', done => {
const input = {
somePointer: { __type: 'Pointer', className: 'Micro', objectId: 'oft' },
};
var output = transform.parseObjectToMongoObjectForCreate(null, input, {
fields: {somePointer: {type: 'Pointer'}}
const output = transform.parseObjectToMongoObjectForCreate(null, input, {
fields: { somePointer: { type: 'Pointer' } },
});
expect(typeof output._p_somePointer).toEqual('string');
expect(output._p_somePointer).toEqual('Micro$oft');
done();
});
it('changes existing pointer keys', (done) => {
var input = {
userPointer: {__type: 'Pointer', className: '_User', objectId: 'qwerty'}
it('changes existing pointer keys', done => {
const input = {
userPointer: {
__type: 'Pointer',
className: '_User',
objectId: 'qwerty',
},
};
var output = transform.parseObjectToMongoObjectForCreate(null, input, {
fields: {userPointer: {type: 'Pointer'}}
const output = transform.parseObjectToMongoObjectForCreate(null, input, {
fields: { userPointer: { type: 'Pointer' } },
});
expect(typeof output._p_userPointer).toEqual('string');
expect(output._p_userPointer).toEqual('_User$qwerty');
done();
});
it('writes the old ACL format in addition to rperm and wperm on create', (done) => {
var input = {
it('writes the old ACL format in addition to rperm and wperm on create', done => {
const input = {
_rperm: ['*'],
_wperm: ['Kevin'],
};
var output = transform.parseObjectToMongoObjectForCreate(null, input, { fields: {} });
const output = transform.parseObjectToMongoObjectForCreate(null, input, {
fields: {},
});
expect(typeof output._acl).toEqual('object');
expect(output._acl['Kevin'].w).toBeTruthy();
expect(output._acl['Kevin'].r).toBeUndefined();
@@ -236,11 +322,11 @@ describe('parseObjectToMongoObjectForCreate', () => {
done();
});
it('removes Relation types', (done) => {
var input = {
it('removes Relation types', done => {
const input = {
aRelation: { __type: 'Relation', className: 'Stuff' },
};
var output = transform.parseObjectToMongoObjectForCreate(null, input, {
const output = transform.parseObjectToMongoObjectForCreate(null, input, {
fields: {
aRelation: { __type: 'Relation', className: 'Stuff' },
},
@@ -249,14 +335,14 @@ describe('parseObjectToMongoObjectForCreate', () => {
done();
});
it('writes the old ACL format in addition to rperm and wperm on update', (done) => {
var input = {
it('writes the old ACL format in addition to rperm and wperm on update', done => {
const input = {
_rperm: ['*'],
_wperm: ['Kevin']
_wperm: ['Kevin'],
};
var output = transform.transformUpdate(null, input, { fields: {} });
var set = output.$set;
const output = transform.transformUpdate(null, input, { fields: {} });
const set = output.$set;
expect(typeof set).toEqual('object');
expect(typeof set._acl).toEqual('object');
expect(set._acl['Kevin'].w).toBeTruthy();
@@ -266,24 +352,26 @@ describe('parseObjectToMongoObjectForCreate', () => {
done();
});
it('untransforms from _rperm and _wperm to ACL', (done) => {
var input = {
_rperm: ["*"],
_wperm: ["Kevin"]
it('untransforms from _rperm and _wperm to ACL', done => {
const input = {
_rperm: ['*'],
_wperm: ['Kevin'],
};
var output = transform.mongoObjectToParseObject(null, input, { fields: {} });
const output = transform.mongoObjectToParseObject(null, input, {
fields: {},
});
expect(output._rperm).toEqual(['*']);
expect(output._wperm).toEqual(['Kevin']);
expect(output.ACL).toBeUndefined()
expect(output.ACL).toBeUndefined();
done();
});
it('untransforms mongodb number types', (done) => {
var input = {
it('untransforms mongodb number types', done => {
const input = {
long: mongodb.Long.fromNumber(Number.MAX_SAFE_INTEGER),
double: new mongodb.Double(Number.MAX_VALUE)
}
var output = transform.mongoObjectToParseObject(null, input, {
double: new mongodb.Double(Number.MAX_VALUE),
};
const output = transform.mongoObjectToParseObject(null, input, {
fields: {
long: { type: 'Number' },
double: { type: 'Number' },
@@ -294,39 +382,111 @@ describe('parseObjectToMongoObjectForCreate', () => {
done();
});
it('Date object where iso attribute is of type Date', (done) => {
var input = {
ts : { __type: 'Date', iso: new Date('2017-01-18T00:00:00.000Z') }
}
var output = transform.mongoObjectToParseObject(null, input, {
fields : {
ts : { type : 'Date' }
}
it('Date object where iso attribute is of type Date', done => {
const input = {
ts: { __type: 'Date', iso: new Date('2017-01-18T00:00:00.000Z') },
};
const output = transform.mongoObjectToParseObject(null, input, {
fields: {
ts: { type: 'Date' },
},
});
expect(output.ts.iso).toEqual('2017-01-18T00:00:00.000Z');
done();
});
it('Date object where iso attribute is of type String', (done) => {
var input = {
ts : { __type: 'Date', iso: '2017-01-18T00:00:00.000Z' }
}
var output = transform.mongoObjectToParseObject(null, input, {
fields : {
ts : { type : 'Date' }
}
it('Date object where iso attribute is of type String', done => {
const input = {
ts: { __type: 'Date', iso: '2017-01-18T00:00:00.000Z' },
};
const output = transform.mongoObjectToParseObject(null, input, {
fields: {
ts: { type: 'Date' },
},
});
expect(output.ts.iso).toEqual('2017-01-18T00:00:00.000Z');
done();
});
it('object with undefined nested values', () => {
const input = {
_id: 'vQHyinCW1l',
urls: { firstUrl: 'https://', secondUrl: undefined },
};
const output = transform.mongoObjectToParseObject(null, input, {
fields: {
urls: { type: 'Object' },
},
});
expect(output.urls).toEqual({
firstUrl: 'https://',
secondUrl: undefined,
});
});
it('undefined objects', () => {
const input = {
_id: 'vQHyinCW1l',
urls: undefined,
};
const output = transform.mongoObjectToParseObject(null, input, {
fields: {
urls: { type: 'Object' },
},
});
expect(output.urls).toBeUndefined();
});
it('$regex in $all list', done => {
const input = {
arrayField: {
$all: [
{ $regex: '^\\Qone\\E' },
{ $regex: '^\\Qtwo\\E' },
{ $regex: '^\\Qthree\\E' },
],
},
};
const outputValue = {
arrayField: { $all: [/^\Qone\E/, /^\Qtwo\E/, /^\Qthree\E/] },
};
const output = transform.transformWhere(null, input);
jequal(outputValue.arrayField, output.arrayField);
done();
});
it('$regex in $all list must be { $regex: "string" }', done => {
const input = {
arrayField: { $all: [{ $regex: 1 }] },
};
expect(() => {
transform.transformWhere(null, input);
}).toThrow();
done();
});
it('all values in $all must be $regex (start with string) or non $regex (start with string)', done => {
const input = {
arrayField: {
$all: [{ $regex: '^\\Qone\\E' }, { $unknown: '^\\Qtwo\\E' }],
},
};
expect(() => {
transform.transformWhere(null, input);
}).toThrow();
done();
});
});
describe('transformUpdate', () => {
it('removes Relation types', (done) => {
var input = {
it('removes Relation types', done => {
const input = {
aRelation: { __type: 'Relation', className: 'Stuff' },
};
var output = transform.transformUpdate(null, input, {
const output = transform.transformUpdate(null, input, {
fields: {
aRelation: { __type: 'Relation', className: 'Stuff' },
},
@@ -335,3 +495,129 @@ describe('transformUpdate', () => {
done();
});
});
describe('transformConstraint', () => {
describe('$relativeTime', () => {
it('should error on $eq, $ne, and $exists', () => {
expect(() => {
transform.transformConstraint({
$eq: {
ttl: {
$relativeTime: '12 days ago',
},
},
});
}).toThrow();
expect(() => {
transform.transformConstraint({
$ne: {
ttl: {
$relativeTime: '12 days ago',
},
},
});
}).toThrow();
expect(() => {
transform.transformConstraint({
$exists: {
$relativeTime: '12 days ago',
},
});
}).toThrow();
});
});
});
describe('relativeTimeToDate', () => {
const now = new Date('2017-09-26T13:28:16.617Z');
describe('In the future', () => {
it('should parse valid natural time', () => {
const text = 'in 1 year 2 weeks 12 days 10 hours 24 minutes 30 seconds';
const { result, status, info } = transform.relativeTimeToDate(text, now);
expect(result.toISOString()).toBe('2018-10-22T23:52:46.617Z');
expect(status).toBe('success');
expect(info).toBe('future');
});
});
describe('In the past', () => {
it('should parse valid natural time', () => {
const text = '2 days 12 hours 1 minute 12 seconds ago';
const { result, status, info } = transform.relativeTimeToDate(text, now);
expect(result.toISOString()).toBe('2017-09-24T01:27:04.617Z');
expect(status).toBe('success');
expect(info).toBe('past');
});
});
describe('From now', () => {
it('should equal current time', () => {
const text = 'now';
const { result, status, info } = transform.relativeTimeToDate(text, now);
expect(result.toISOString()).toBe('2017-09-26T13:28:16.617Z');
expect(status).toBe('success');
expect(info).toBe('present');
});
});
describe('Error cases', () => {
it('should error if string is completely gibberish', () => {
expect(
transform.relativeTimeToDate('gibberishasdnklasdnjklasndkl123j123')
).toEqual({
status: 'error',
info: "Time should either start with 'in' or end with 'ago'",
});
});
it('should error if string contains neither `ago` nor `in`', () => {
expect(transform.relativeTimeToDate('12 hours 1 minute')).toEqual({
status: 'error',
info: "Time should either start with 'in' or end with 'ago'",
});
});
it('should error if there are missing units or numbers', () => {
expect(transform.relativeTimeToDate('in 12 hours 1')).toEqual({
status: 'error',
info: 'Invalid time string. Dangling unit or number.',
});
expect(transform.relativeTimeToDate('12 hours minute ago')).toEqual({
status: 'error',
info: 'Invalid time string. Dangling unit or number.',
});
});
it('should error on floating point numbers', () => {
expect(transform.relativeTimeToDate('in 12.3 hours')).toEqual({
status: 'error',
info: "'12.3' is not an integer.",
});
});
it('should error if numbers are invalid', () => {
expect(transform.relativeTimeToDate('12 hours 123a minute ago')).toEqual({
status: 'error',
info: "'123a' is not an integer.",
});
});
it('should error on invalid interval units', () => {
expect(transform.relativeTimeToDate('4 score 7 years ago')).toEqual({
status: 'error',
info: "Invalid interval: 'score'",
});
});
it("should error when string contains 'ago' and 'in'", () => {
expect(transform.relativeTimeToDate('in 1 day 2 minutes ago')).toEqual({
status: 'error',
info: "Time cannot have both 'in' and 'ago'",
});
});
});
});
+15 -14
View File
@@ -1,12 +1,13 @@
var NullCacheAdapter = require('../src/Adapters/Cache/NullCacheAdapter').default;
const NullCacheAdapter = require('../lib/Adapters/Cache/NullCacheAdapter')
.default;
describe('NullCacheAdapter', function() {
var KEY = 'hello';
var VALUE = 'world';
const KEY = 'hello';
const VALUE = 'world';
it('should expose promisifyed methods', (done) => {
var cache = new NullCacheAdapter({
ttl: NaN
it('should expose promisifyed methods', done => {
const cache = new NullCacheAdapter({
ttl: NaN,
});
// Verify all methods return promises.
@@ -14,24 +15,24 @@ describe('NullCacheAdapter', function() {
cache.put(KEY, VALUE),
cache.del(KEY),
cache.get(KEY),
cache.clear()
cache.clear(),
]).then(() => {
done();
});
});
it('should get/set/clear', (done) => {
var cache = new NullCacheAdapter({
ttl: NaN
it('should get/set/clear', done => {
const cache = new NullCacheAdapter({
ttl: NaN,
});
cache.put(KEY, VALUE)
cache
.put(KEY, VALUE)
.then(() => cache.get(KEY))
.then((value) => expect(value).toEqual(null))
.then(value => expect(value).toEqual(null))
.then(() => cache.clear())
.then(() => cache.get(KEY))
.then((value) => expect(value).toEqual(null))
.then(value => expect(value).toEqual(null))
.then(done);
});
});
+96 -83
View File
@@ -1,141 +1,154 @@
var OAuth = require("../src/Adapters/Auth/OAuth1Client");
const OAuth = require('../lib/Adapters/Auth/OAuth1Client');
describe('OAuth', function() {
it("Nonce should have right length", (done) => {
it('Nonce should have right length', done => {
jequal(OAuth.nonce().length, 30);
done();
});
it("Should properly build parameter string", (done) => {
var string = OAuth.buildParameterString({c:1, a:2, b:3})
jequal(string, "a=2&b=3&c=1");
it('Should properly build parameter string', done => {
const string = OAuth.buildParameterString({ c: 1, a: 2, b: 3 });
jequal(string, 'a=2&b=3&c=1');
done();
});
it("Should properly build empty parameter string", (done) => {
var string = OAuth.buildParameterString()
jequal(string, "");
it('Should properly build empty parameter string', done => {
const string = OAuth.buildParameterString();
jequal(string, '');
done();
});
it("Should properly build signature string", (done) => {
var string = OAuth.buildSignatureString("get", "http://dummy.com", "");
jequal(string, "GET&http%3A%2F%2Fdummy.com&");
it('Should properly build signature string', done => {
const string = OAuth.buildSignatureString('get', 'http://dummy.com', '');
jequal(string, 'GET&http%3A%2F%2Fdummy.com&');
done();
});
it("Should properly generate request signature", (done) => {
var request = {
host: "dummy.com",
path: "path"
it('Should properly generate request signature', done => {
let request = {
host: 'dummy.com',
path: 'path',
};
var oauth_params = {
const oauth_params = {
oauth_timestamp: 123450000,
oauth_nonce: "AAAAAAAAAAAAAAAAA",
oauth_consumer_key: "hello",
oauth_token: "token"
oauth_nonce: 'AAAAAAAAAAAAAAAAA',
oauth_consumer_key: 'hello',
oauth_token: 'token',
};
var consumer_secret = "world";
var auth_token_secret = "secret";
request = OAuth.signRequest(request, oauth_params, consumer_secret, auth_token_secret);
jequal(request.headers['Authorization'], 'OAuth oauth_consumer_key="hello", oauth_nonce="AAAAAAAAAAAAAAAAA", oauth_signature="8K95bpQcDi9Nd2GkhumTVcw4%2BXw%3D", oauth_signature_method="HMAC-SHA1", oauth_timestamp="123450000", oauth_token="token", oauth_version="1.0"');
const consumer_secret = 'world';
const auth_token_secret = 'secret';
request = OAuth.signRequest(
request,
oauth_params,
consumer_secret,
auth_token_secret
);
jequal(
request.headers['Authorization'],
'OAuth oauth_consumer_key="hello", oauth_nonce="AAAAAAAAAAAAAAAAA", oauth_signature="8K95bpQcDi9Nd2GkhumTVcw4%2BXw%3D", oauth_signature_method="HMAC-SHA1", oauth_timestamp="123450000", oauth_token="token", oauth_version="1.0"'
);
done();
});
it("Should properly build request", (done) => {
var options = {
host: "dummy.com",
consumer_key: "hello",
consumer_secret: "world",
auth_token: "token",
auth_token_secret: "secret",
it('Should properly build request', done => {
const options = {
host: 'dummy.com',
consumer_key: 'hello',
consumer_secret: 'world',
auth_token: 'token',
auth_token_secret: 'secret',
// Custom oauth params for tests
oauth_params: {
oauth_timestamp: 123450000,
oauth_nonce: "AAAAAAAAAAAAAAAAA"
}
oauth_nonce: 'AAAAAAAAAAAAAAAAA',
},
};
var path = "path";
var method = "get";
const path = 'path';
const method = 'get';
var oauthClient = new OAuth(options);
var req = oauthClient.buildRequest(method, path, {"query": "param"});
const oauthClient = new OAuth(options);
const req = oauthClient.buildRequest(method, path, { query: 'param' });
jequal(req.host, options.host);
jequal(req.path, "/" + path + "?query=param");
jequal(req.method, "GET");
jequal(req.path, '/' + path + '?query=param');
jequal(req.method, 'GET');
jequal(req.headers['Content-Type'], 'application/x-www-form-urlencoded');
jequal(req.headers['Authorization'], 'OAuth oauth_consumer_key="hello", oauth_nonce="AAAAAAAAAAAAAAAAA", oauth_signature="wNkyEkDE%2F0JZ2idmqyrgHdvC0rs%3D", oauth_signature_method="HMAC-SHA1", oauth_timestamp="123450000", oauth_token="token", oauth_version="1.0"')
jequal(
req.headers['Authorization'],
'OAuth oauth_consumer_key="hello", oauth_nonce="AAAAAAAAAAAAAAAAA", oauth_signature="wNkyEkDE%2F0JZ2idmqyrgHdvC0rs%3D", oauth_signature_method="HMAC-SHA1", oauth_timestamp="123450000", oauth_token="token", oauth_version="1.0"'
);
done();
});
function validateCannotAuthenticateError(data, done) {
jequal(typeof data, "object");
jequal(typeof data.errors, "object");
var errors = data.errors;
jequal(typeof errors[0], "object");
jequal(typeof data, 'object');
jequal(typeof data.errors, 'object');
const errors = data.errors;
jequal(typeof errors[0], 'object');
// Cannot authenticate error
jequal(errors[0].code, 32);
done();
}
it("Should fail a GET request", (done) => {
var options = {
host: "api.twitter.com",
consumer_key: "XXXXXXXXXXXXXXXXXXXXXXXXX",
consumer_secret: "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX",
it('Should fail a GET request', done => {
const options = {
host: 'api.twitter.com',
consumer_key: 'XXXXXXXXXXXXXXXXXXXXXXXXX',
consumer_secret: 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX',
};
var path = "/1.1/help/configuration.json";
var params = {"lang": "en"};
var oauthClient = new OAuth(options);
oauthClient.get(path, params).then(function(data){
const path = '/1.1/help/configuration.json';
const params = { lang: 'en' };
const oauthClient = new OAuth(options);
oauthClient.get(path, params).then(function(data) {
validateCannotAuthenticateError(data, done);
})
});
});
it("Should fail a POST request", (done) => {
var options = {
host: "api.twitter.com",
consumer_key: "XXXXXXXXXXXXXXXXXXXXXXXXX",
consumer_secret: "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX",
it('Should fail a POST request', done => {
const options = {
host: 'api.twitter.com',
consumer_key: 'XXXXXXXXXXXXXXXXXXXXXXXXX',
consumer_secret: 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX',
};
var body = {
lang: "en"
const body = {
lang: 'en',
};
var path = "/1.1/account/settings.json";
const path = '/1.1/account/settings.json';
var oauthClient = new OAuth(options);
oauthClient.post(path, null, body).then(function(data){
const oauthClient = new OAuth(options);
oauthClient.post(path, null, body).then(function(data) {
validateCannotAuthenticateError(data, done);
})
});
});
it("Should fail a request", (done) => {
var options = {
host: "localhost",
consumer_key: "XXXXXXXXXXXXXXXXXXXXXXXXX",
consumer_secret: "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX",
it('Should fail a request', done => {
const options = {
host: 'localhost',
consumer_key: 'XXXXXXXXXXXXXXXXXXXXXXXXX',
consumer_secret: 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX',
};
var body = {
lang: "en"
const body = {
lang: 'en',
};
var path = "/";
const path = '/';
var oauthClient = new OAuth(options);
oauthClient.post(path, null, body).then(function(){
jequal(false, true);
done();
}).catch(function(){
jequal(true, true);
done();
})
const oauthClient = new OAuth(options);
oauthClient
.post(path, null, body)
.then(function() {
jequal(false, true);
done();
})
.catch(function() {
jequal(true, true);
done();
});
});
it("Should fail with missing options", (done) => {
var options = undefined;
it('Should fail with missing options', done => {
const options = undefined;
try {
new OAuth(options);
} catch (error) {
+445 -112
View File
@@ -1,24 +1,24 @@
'use strict';
const request = require('request');
const request = require('../lib/request');
const delayPromise = (delay) => {
return new Promise((resolve) => {
const delayPromise = delay => {
return new Promise(resolve => {
setTimeout(resolve, delay);
});
}
};
describe('Parse.Push', () => {
var setup = function() {
var sendToInstallationSpy = jasmine.createSpy();
const setup = function() {
const sendToInstallationSpy = jasmine.createSpy();
var pushAdapter = {
const pushAdapter = {
send: function(body, installations) {
var badge = body.data.badge;
const promises = installations.map((installation) => {
const badge = body.data.badge;
const promises = installations.map(installation => {
sendToInstallationSpy(installation);
if (installation.deviceType == "ios") {
if (installation.deviceType == 'ios') {
expect(installation.badge).toEqual(badge);
expect(installation.originalBadge + 1).toEqual(installation.badge);
} else {
@@ -27,33 +27,39 @@ describe('Parse.Push', () => {
return Promise.resolve({
err: null,
device: installation,
transmitted: true
})
transmitted: true,
});
});
return Promise.all(promises);
},
getValidPushTypes: function() {
return ["ios", "android"];
}
}
return ['ios', 'android'];
},
};
return reconfigureServer({
appId: Parse.applicationId,
masterKey: Parse.masterKey,
serverURL: Parse.serverURL,
push: {
adapter: pushAdapter
}
adapter: pushAdapter,
},
})
.then(() => {
var installations = [];
while(installations.length != 10) {
var installation = new Parse.Object("_Installation");
installation.set("installationId", "installation_" + installations.length);
installation.set("deviceToken","device_token_" + installations.length)
installation.set("badge", installations.length);
installation.set("originalBadge", installations.length);
installation.set("deviceType", "ios");
const installations = [];
while (installations.length != 10) {
const installation = new Parse.Object('_Installation');
installation.set(
'installationId',
'installation_' + installations.length
);
installation.set(
'deviceToken',
'device_token_' + installations.length
);
installation.set('badge', installations.length);
installation.set('originalBadge', installations.length);
installation.set('deviceType', 'ios');
installations.push(installation);
}
return Parse.Object.saveAll(installations);
@@ -63,82 +69,102 @@ describe('Parse.Push', () => {
sendToInstallationSpy,
};
})
.catch((err) => {
.catch(err => {
console.error(err);
throw err;
})
}
});
};
it('should properly send push', (done) => {
return setup().then(({ sendToInstallationSpy }) => {
return Parse.Push.send({
where: {
deviceType: 'ios'
},
data: {
badge: 'Increment',
alert: 'Hello world!'
}
}, {useMasterKey: true})
.then(() => {
return delayPromise(500);
})
.then(() => {
expect(sendToInstallationSpy.calls.count()).toEqual(10);
})
}).then(() => {
done();
}).catch((err) => {
jfail(err);
done();
});
it('should properly send push', done => {
return setup()
.then(({ sendToInstallationSpy }) => {
return Parse.Push.send(
{
where: {
deviceType: 'ios',
},
data: {
badge: 'Increment',
alert: 'Hello world!',
},
},
{ useMasterKey: true }
)
.then(() => {
return delayPromise(500);
})
.then(() => {
expect(sendToInstallationSpy.calls.count()).toEqual(10);
});
})
.then(() => {
done();
})
.catch(err => {
jfail(err);
done();
});
});
it('should properly send push with lowercaseIncrement', (done) => {
return setup().then(() => {
return Parse.Push.send({
where: {
deviceType: 'ios'
},
data: {
badge: 'increment',
alert: 'Hello world!'
}
}, {useMasterKey: true})
}).then(() => {
return delayPromise(500);
}).then(() => {
done();
}).catch((err) => {
jfail(err);
done();
});
it('should properly send push with lowercaseIncrement', done => {
return setup()
.then(() => {
return Parse.Push.send(
{
where: {
deviceType: 'ios',
},
data: {
badge: 'increment',
alert: 'Hello world!',
},
},
{ useMasterKey: true }
);
})
.then(() => {
return delayPromise(500);
})
.then(() => {
done();
})
.catch(err => {
jfail(err);
done();
});
});
it('should not allow clients to query _PushStatus', done => {
setup()
.then(() => Parse.Push.send({
where: {
deviceType: 'ios'
},
data: {
badge: 'increment',
alert: 'Hello world!'
}
}, {useMasterKey: true}))
.then(() =>
Parse.Push.send(
{
where: {
deviceType: 'ios',
},
data: {
badge: 'increment',
alert: 'Hello world!',
},
},
{ useMasterKey: true }
)
)
.then(() => delayPromise(500))
.then(() => {
request.get({
request({
url: 'http://localhost:8378/1/classes/_PushStatus',
json: true,
headers: {
'X-Parse-Application-Id': 'test',
},
}, (error, response, body) => {
expect(body.error).toEqual('unauthorized');
}).then(fail, response => {
expect(response.data.error).toEqual('unauthorized');
done();
});
}).catch((err) => {
})
.catch(err => {
jfail(err);
done();
});
@@ -146,59 +172,366 @@ describe('Parse.Push', () => {
it('should allow master key to query _PushStatus', done => {
setup()
.then(() => Parse.Push.send({
where: {
deviceType: 'ios'
},
data: {
badge: 'increment',
alert: 'Hello world!'
}
}, {useMasterKey: true}))
.then(() =>
Parse.Push.send(
{
where: {
deviceType: 'ios',
},
data: {
badge: 'increment',
alert: 'Hello world!',
},
},
{ useMasterKey: true }
)
)
.then(() => delayPromise(500)) // put a delay as we keep writing
.then(() => {
request.get({
request({
url: 'http://localhost:8378/1/classes/_PushStatus',
json: true,
headers: {
'X-Parse-Application-Id': 'test',
'X-Parse-Master-Key': 'test',
},
}, (error, response, body) => {
}).then(response => {
const body = response.data;
try {
expect(body.results.length).toEqual(1);
expect(body.results[0].query).toEqual('{"deviceType":"ios"}');
expect(body.results[0].payload).toEqual('{"badge":"increment","alert":"Hello world!"}');
} catch(e) {
expect(body.results[0].payload).toEqual(
'{"badge":"increment","alert":"Hello world!"}'
);
} catch (e) {
jfail(e);
}
done();
});
}).catch((err) => {
})
.catch(err => {
jfail(err);
done();
});
});
it('should throw error if missing push configuration', done => {
reconfigureServer({push: null})
reconfigureServer({ push: null })
.then(() => {
return Parse.Push.send({
where: {
deviceType: 'ios'
return Parse.Push.send(
{
where: {
deviceType: 'ios',
},
data: {
badge: 'increment',
alert: 'Hello world!',
},
},
data: {
badge: 'increment',
alert: 'Hello world!'
}
}, {useMasterKey: true})
}).then(() => {
fail('should not succeed');
}, (err) => {
expect(err.code).toEqual(Parse.Error.PUSH_MISCONFIGURED);
done();
}).catch((err) => {
{ useMasterKey: true }
);
})
.then(
() => {
fail('should not succeed');
},
err => {
expect(err.code).toEqual(Parse.Error.PUSH_MISCONFIGURED);
done();
}
)
.catch(err => {
jfail(err);
done();
});
});
const successfulAny = function(body, installations) {
const promises = installations.map(device => {
return Promise.resolve({
transmitted: true,
device: device,
});
});
return Promise.all(promises);
};
const provideInstallations = function(num) {
if (!num) {
num = 2;
}
const installations = [];
while (installations.length !== num) {
// add Android installations
const installation = new Parse.Object('_Installation');
installation.set(
'installationId',
'installation_' + installations.length
);
installation.set('deviceToken', 'device_token_' + installations.length);
installation.set('deviceType', 'android');
installations.push(installation);
}
return installations;
};
const losingAdapter = {
send: function(body, installations) {
// simulate having lost an installation before this was called
// thus invalidating our 'count' in _PushStatus
installations.pop();
return successfulAny(body, installations);
},
getValidPushTypes: function() {
return ['android'];
},
};
/**
* Verifies that _PushStatus cannot get stuck in a 'running' state
* Simulates a simple push where 1 installation is removed between _PushStatus
* count being set and the pushes being sent
*/
it("does not get stuck with _PushStatus 'running' on 1 installation lost", done => {
reconfigureServer({
push: { adapter: losingAdapter },
})
.then(() => {
return Parse.Object.saveAll(provideInstallations());
})
.then(() => {
return Parse.Push.send(
{
data: { alert: 'We fixed our status!' },
where: { deviceType: 'android' },
},
{ useMasterKey: true }
);
})
.then(() => {
// it is enqueued so it can take time
return new Promise(resolve => {
setTimeout(() => {
resolve();
}, 1000);
});
})
.then(() => {
// query for push status
const query = new Parse.Query('_PushStatus');
return query.find({ useMasterKey: true });
})
.then(results => {
// verify status is NOT broken
expect(results.length).toBe(1);
const result = results[0];
expect(result.get('status')).toEqual('succeeded');
expect(result.get('numSent')).toEqual(1);
expect(result.get('count')).toEqual(undefined);
done();
});
});
/**
* Verifies that _PushStatus cannot get stuck in a 'running' state
* Simulates a simple push where 1 installation is added between _PushStatus
* count being set and the pushes being sent
*/
it("does not get stuck with _PushStatus 'running' on 1 installation added", done => {
const installations = provideInstallations();
// add 1 iOS installation which we will omit & add later on
const iOSInstallation = new Parse.Object('_Installation');
iOSInstallation.set(
'installationId',
'installation_' + installations.length
);
iOSInstallation.set('deviceToken', 'device_token_' + installations.length);
iOSInstallation.set('deviceType', 'ios');
installations.push(iOSInstallation);
reconfigureServer({
push: {
adapter: {
send: function(body, installations) {
// simulate having added an installation before this was called
// thus invalidating our 'count' in _PushStatus
installations.push(iOSInstallation);
return successfulAny(body, installations);
},
getValidPushTypes: function() {
return ['android'];
},
},
},
})
.then(() => {
return Parse.Object.saveAll(installations);
})
.then(() => {
return Parse.Push.send(
{
data: { alert: 'We fixed our status!' },
where: { deviceType: { $ne: 'random' } },
},
{ useMasterKey: true }
);
})
.then(() => {
// it is enqueued so it can take time
return new Promise(resolve => {
setTimeout(() => {
resolve();
}, 1000);
});
})
.then(() => {
// query for push status
const query = new Parse.Query('_PushStatus');
return query.find({ useMasterKey: true });
})
.then(results => {
// verify status is NOT broken
expect(results.length).toBe(1);
const result = results[0];
expect(result.get('status')).toEqual('succeeded');
expect(result.get('numSent')).toEqual(3);
expect(result.get('count')).toEqual(undefined);
done();
});
});
/**
* Verifies that _PushStatus cannot get stuck in a 'running' state
* Simulates an extended push, where some installations may be removed,
* resulting in a non-zero count
*/
it("does not get stuck with _PushStatus 'running' on many installations removed", done => {
const devices = 1000;
const installations = provideInstallations(devices);
reconfigureServer({
push: { adapter: losingAdapter },
})
.then(() => {
return Parse.Object.saveAll(installations);
})
.then(() => {
return Parse.Push.send(
{
data: { alert: 'We fixed our status!' },
where: { deviceType: 'android' },
},
{ useMasterKey: true }
);
})
.then(() => {
// it is enqueued so it can take time
return new Promise(resolve => {
setTimeout(() => {
resolve();
}, 1000);
});
})
.then(() => {
// query for push status
const query = new Parse.Query('_PushStatus');
return query.find({ useMasterKey: true });
})
.then(results => {
// verify status is NOT broken
expect(results.length).toBe(1);
const result = results[0];
expect(result.get('status')).toEqual('succeeded');
// expect # less than # of batches used, assuming each batch is 100 pushes
expect(result.get('numSent')).toEqual(devices - devices / 100);
expect(result.get('count')).toEqual(undefined);
done();
});
});
/**
* Verifies that _PushStatus cannot get stuck in a 'running' state
* Simulates an extended push, where some installations may be added,
* resulting in a non-zero count
*/
it("does not get stuck with _PushStatus 'running' on many installations added", done => {
const devices = 1000;
const installations = provideInstallations(devices);
// add 1 iOS installation which we will omit & add later on
const iOSInstallations = [];
while (iOSInstallations.length !== devices / 100) {
const iOSInstallation = new Parse.Object('_Installation');
iOSInstallation.set(
'installationId',
'installation_' + installations.length
);
iOSInstallation.set(
'deviceToken',
'device_token_' + installations.length
);
iOSInstallation.set('deviceType', 'ios');
installations.push(iOSInstallation);
iOSInstallations.push(iOSInstallation);
}
reconfigureServer({
push: {
adapter: {
send: function(body, installations) {
// simulate having added an installation before this was called
// thus invalidating our 'count' in _PushStatus
installations.push(iOSInstallations.pop());
return successfulAny(body, installations);
},
getValidPushTypes: function() {
return ['android'];
},
},
},
})
.then(() => {
return Parse.Object.saveAll(installations);
})
.then(() => {
return Parse.Push.send(
{
data: { alert: 'We fixed our status!' },
where: { deviceType: { $ne: 'random' } },
},
{ useMasterKey: true }
);
})
.then(() => {
// it is enqueued so it can take time
return new Promise(resolve => {
setTimeout(() => {
resolve();
}, 1000);
});
})
.then(() => {
// query for push status
const query = new Parse.Query('_PushStatus');
return query.find({ useMasterKey: true });
})
.then(results => {
// verify status is NOT broken
expect(results.length).toBe(1);
const result = results[0];
expect(result.get('status')).toEqual('succeeded');
// expect # less than # of batches used, assuming each batch is 100 pushes
expect(result.get('numSent')).toEqual(devices + devices / 100);
expect(result.get('count')).toEqual(undefined);
done();
});
});
});
+874 -1206
View File
File diff suppressed because it is too large Load Diff
+1079 -838
View File
File diff suppressed because it is too large Load Diff
+35 -24
View File
@@ -1,69 +1,80 @@
var ParseCloudCodePublisher = require('../src/LiveQuery/ParseCloudCodePublisher').ParseCloudCodePublisher;
var Parse = require('parse/node');
const ParseCloudCodePublisher = require('../lib/LiveQuery/ParseCloudCodePublisher')
.ParseCloudCodePublisher;
const Parse = require('parse/node');
describe('ParseCloudCodePublisher', function() {
beforeEach(function(done) {
// Mock ParsePubSub
var mockParsePubSub = {
const mockParsePubSub = {
createPublisher: jasmine.createSpy('publish').and.returnValue({
publish: jasmine.createSpy('publish'),
on: jasmine.createSpy('on')
on: jasmine.createSpy('on'),
}),
createSubscriber: jasmine.createSpy('publish').and.returnValue({
subscribe: jasmine.createSpy('subscribe'),
on: jasmine.createSpy('on')
})
on: jasmine.createSpy('on'),
}),
};
jasmine.mockLibrary('../src/LiveQuery/ParsePubSub', 'ParsePubSub', mockParsePubSub);
jasmine.mockLibrary(
'../lib/LiveQuery/ParsePubSub',
'ParsePubSub',
mockParsePubSub
);
done();
});
it('can initialize', function() {
var config = {}
const config = {};
new ParseCloudCodePublisher(config);
var ParsePubSub = require('../src/LiveQuery/ParsePubSub').ParsePubSub;
const ParsePubSub = require('../lib/LiveQuery/ParsePubSub').ParsePubSub;
expect(ParsePubSub.createPublisher).toHaveBeenCalledWith(config);
});
it('can handle cloud code afterSave request', function() {
var publisher = new ParseCloudCodePublisher({});
const publisher = new ParseCloudCodePublisher({});
publisher._onCloudCodeMessage = jasmine.createSpy('onCloudCodeMessage');
var request = {};
const request = {};
publisher.onCloudCodeAfterSave(request);
expect(publisher._onCloudCodeMessage).toHaveBeenCalledWith(Parse.applicationId + 'afterSave', request);
expect(publisher._onCloudCodeMessage).toHaveBeenCalledWith(
Parse.applicationId + 'afterSave',
request
);
});
it('can handle cloud code afterDelete request', function() {
var publisher = new ParseCloudCodePublisher({});
const publisher = new ParseCloudCodePublisher({});
publisher._onCloudCodeMessage = jasmine.createSpy('onCloudCodeMessage');
var request = {};
const request = {};
publisher.onCloudCodeAfterDelete(request);
expect(publisher._onCloudCodeMessage).toHaveBeenCalledWith(Parse.applicationId + 'afterDelete', request);
expect(publisher._onCloudCodeMessage).toHaveBeenCalledWith(
Parse.applicationId + 'afterDelete',
request
);
});
it('can handle cloud code request', function() {
var publisher = new ParseCloudCodePublisher({});
var currentParseObject = new Parse.Object('Test');
const publisher = new ParseCloudCodePublisher({});
const currentParseObject = new Parse.Object('Test');
currentParseObject.set('key', 'value');
var originalParseObject = new Parse.Object('Test');
const originalParseObject = new Parse.Object('Test');
originalParseObject.set('key', 'originalValue');
var request = {
const request = {
object: currentParseObject,
original: originalParseObject
original: originalParseObject,
};
publisher._onCloudCodeMessage('afterSave', request);
var args = publisher.parsePublisher.publish.calls.mostRecent().args;
const args = publisher.parsePublisher.publish.calls.mostRecent().args;
expect(args[0]).toBe('afterSave');
var message = JSON.parse(args[1]);
const message = JSON.parse(args[1]);
expect(message.currentParseObject).toEqual(request.object._toFullJSON());
expect(message.originalParseObject).toEqual(request.original._toFullJSON());
});
afterEach(function(){
jasmine.restoreLibrary('../src/LiveQuery/ParsePubSub', 'ParsePubSub');
afterEach(function() {
jasmine.restoreLibrary('../lib/LiveQuery/ParsePubSub', 'ParsePubSub');
});
});
+642 -369
View File
File diff suppressed because it is too large Load Diff
+680 -517
View File
File diff suppressed because it is too large Load Diff
+132 -116
View File
@@ -1,74 +1,88 @@
'use strict';
var request = require('request');
const Config = require('../src/Config');
const request = require('../lib/request');
const Config = require('../lib/Config');
describe('a GlobalConfig', () => {
beforeEach(done => {
const config = new Config('test');
const query = on_db('mongo', () => {
// Legacy is with an int...
return { objectId: 1 };
}, () => {
return { objectId: "1" }
})
config.database.adapter.upsertOneObject(
'_GlobalConfig',
{ fields: { objectId: { type: 'Number' }, params: {type: 'Object'}} },
query,
{ params: { companies: ['US', 'DK'] } }
).then(done, (err) => {
jfail(err);
done();
});
const config = Config.get('test');
const query = on_db(
'mongo',
() => {
// Legacy is with an int...
return { objectId: 1 };
},
() => {
return { objectId: '1' };
}
);
config.database.adapter
.upsertOneObject(
'_GlobalConfig',
{
fields: { objectId: { type: 'Number' }, params: { type: 'Object' } },
},
query,
{ params: { companies: ['US', 'DK'] } }
)
.then(done, err => {
jfail(err);
done();
});
});
it('can be retrieved', (done) => {
request.get({
url : 'http://localhost:8378/1/config',
json : true,
headers: {
'X-Parse-Application-Id': 'test',
'X-Parse-Master-Key' : 'test'
}
}, (error, response, body) => {
const headers = {
'Content-Type': 'application/json',
'X-Parse-Application-Id': 'test',
'X-Parse-Master-Key': 'test',
};
it('can be retrieved', done => {
request({
url: 'http://localhost:8378/1/config',
json: true,
headers,
}).then(response => {
const body = response.data;
try {
expect(response.statusCode).toEqual(200);
expect(response.status).toEqual(200);
expect(body.params.companies).toEqual(['US', 'DK']);
} catch(e) { jfail(e); }
} catch (e) {
jfail(e);
}
done();
});
});
it('can be updated when a master key exists', (done) => {
request.put({
url : 'http://localhost:8378/1/config',
json : true,
body : { params: { companies: ['US', 'DK', 'SE'] } },
headers: {
'X-Parse-Application-Id': 'test',
'X-Parse-Master-Key' : 'test'
}
}, (error, response, body) => {
expect(response.statusCode).toEqual(200);
it('can be updated when a master key exists', done => {
request({
method: 'PUT',
url: 'http://localhost:8378/1/config',
json: true,
body: { params: { companies: ['US', 'DK', 'SE'] } },
headers,
}).then(response => {
const body = response.data;
expect(response.status).toEqual(200);
expect(body.result).toEqual(true);
done();
});
});
it('can add and retrive files', (done) => {
request.put({
url : 'http://localhost:8378/1/config',
json : true,
body : { params: { file: { __type: 'File', name: 'name', url: 'http://url' } } },
headers: {
'X-Parse-Application-Id': 'test',
'X-Parse-Master-Key' : 'test'
}
}, (error, response, body) => {
expect(response.statusCode).toEqual(200);
it('can add and retrive files', done => {
request({
method: 'PUT',
url: 'http://localhost:8378/1/config',
json: true,
body: {
params: { file: { __type: 'File', name: 'name', url: 'http://url' } },
},
headers,
}).then(response => {
const body = response.data;
expect(response.status).toEqual(200);
expect(body.result).toEqual(true);
Parse.Config.get().then((res) => {
Parse.Config.get().then(res => {
const file = res.get('file');
expect(file.name()).toBe('name');
expect(file.url()).toBe('http://url');
@@ -77,20 +91,19 @@ describe('a GlobalConfig', () => {
});
});
it('can add and retrive Geopoints', (done) => {
const geopoint = new Parse.GeoPoint(10,-20);
request.put({
url : 'http://localhost:8378/1/config',
json : true,
body : { params: { point: geopoint.toJSON() } },
headers: {
'X-Parse-Application-Id': 'test',
'X-Parse-Master-Key' : 'test'
}
}, (error, response, body) => {
expect(response.statusCode).toEqual(200);
it('can add and retrive Geopoints', done => {
const geopoint = new Parse.GeoPoint(10, -20);
request({
method: 'PUT',
url: 'http://localhost:8378/1/config',
json: true,
body: { params: { point: geopoint.toJSON() } },
headers,
}).then(response => {
const body = response.data;
expect(response.status).toEqual(200);
expect(body.result).toEqual(true);
Parse.Config.get().then((res) => {
Parse.Config.get().then(res => {
const point = res.get('point');
expect(point.latitude).toBe(10);
expect(point.longitude).toBe(-20);
@@ -99,75 +112,78 @@ describe('a GlobalConfig', () => {
});
});
it('properly handles delete op', (done) => {
request.put({
url : 'http://localhost:8378/1/config',
json : true,
body : { params: { companies: {__op: 'Delete'}, foo: 'bar' } },
headers: {
'X-Parse-Application-Id': 'test',
'X-Parse-Master-Key' : 'test'
}
}, (error, response, body) => {
expect(response.statusCode).toEqual(200);
it('properly handles delete op', done => {
request({
method: 'PUT',
url: 'http://localhost:8378/1/config',
json: true,
body: { params: { companies: { __op: 'Delete' }, foo: 'bar' } },
headers,
}).then(response => {
const body = response.data;
expect(response.status).toEqual(200);
expect(body.result).toEqual(true);
request.get({
url : 'http://localhost:8378/1/config',
json : true,
headers: {
'X-Parse-Application-Id': 'test',
'X-Parse-Master-Key' : 'test'
}
}, (error, response, body) => {
request({
url: 'http://localhost:8378/1/config',
json: true,
headers,
}).then(response => {
const body = response.data;
try {
expect(response.statusCode).toEqual(200);
expect(response.status).toEqual(200);
expect(body.params.companies).toBeUndefined();
expect(body.params.foo).toBe('bar');
expect(Object.keys(body.params).length).toBe(1);
} catch(e) { jfail(e); }
} catch (e) {
jfail(e);
}
done();
});
});
});
it('fail to update if master key is missing', (done) => {
request.put({
url : 'http://localhost:8378/1/config',
json : true,
body : { params: { companies: [] } },
it('fail to update if master key is missing', done => {
request({
method: 'PUT',
url: 'http://localhost:8378/1/config',
json: true,
body: { params: { companies: [] } },
headers: {
'X-Parse-Application-Id': 'test',
'X-Parse-REST-API-Key' : 'rest'
}
}, (error, response, body) => {
expect(response.statusCode).toEqual(403);
'X-Parse-REST-API-Key': 'rest',
'Content-Type': 'application/json',
},
}).then(fail, response => {
const body = response.data;
expect(response.status).toEqual(403);
expect(body.error).toEqual('unauthorized: master key is required');
done();
});
});
it('failed getting config when it is missing', (done) => {
const config = new Config('test');
config.database.adapter.deleteObjectsByQuery(
'_GlobalConfig',
{ fields: { params: { __type: 'String' } } },
{ objectId: "1" }
).then(() => {
request.get({
url : 'http://localhost:8378/1/config',
json : true,
headers: {
'X-Parse-Application-Id': 'test',
'X-Parse-Master-Key' : 'test'
}
}, (error, response, body) => {
expect(response.statusCode).toEqual(200);
expect(body.params).toEqual({});
it('failed getting config when it is missing', done => {
const config = Config.get('test');
config.database.adapter
.deleteObjectsByQuery(
'_GlobalConfig',
{ fields: { params: { __type: 'String' } } },
{ objectId: '1' }
)
.then(() => {
request({
url: 'http://localhost:8378/1/config',
json: true,
headers,
}).then(response => {
const body = response.data;
expect(response.status).toEqual(200);
expect(body.params).toEqual({});
done();
});
})
.catch(e => {
jfail(e);
done();
});
}).catch((e) => {
jfail(e);
done();
});
});
});
+691 -421
View File
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+1734 -1646
View File
File diff suppressed because it is too large Load Diff
+416
View File
@@ -0,0 +1,416 @@
const TestObject = Parse.Object.extend('TestObject');
const MongoStorageAdapter = require('../lib/Adapters/Storage/Mongo/MongoStorageAdapter')
.default;
const mongoURI =
'mongodb://localhost:27017/parseServerMongoAdapterTestDatabase';
const request = require('../lib/request');
const defaultHeaders = {
'X-Parse-Application-Id': 'test',
'X-Parse-Rest-API-Key': 'rest',
'Content-Type': 'application/json',
};
describe('Parse.Polygon testing', () => {
beforeAll(() => require('../lib/TestUtils').destroyAllDataPermanently());
it('polygon save open path', done => {
const coords = [[0, 0], [0, 1], [1, 1], [1, 0]];
const closed = [[0, 0], [0, 1], [1, 1], [1, 0], [0, 0]];
const obj = new TestObject();
obj.set('polygon', new Parse.Polygon(coords));
return obj
.save()
.then(() => {
const query = new Parse.Query(TestObject);
return query.get(obj.id);
})
.then(result => {
const polygon = result.get('polygon');
equal(polygon instanceof Parse.Polygon, true);
equal(polygon.coordinates, closed);
done();
}, done.fail);
});
it('polygon save closed path', done => {
const coords = [[0, 0], [0, 1], [1, 1], [1, 0], [0, 0]];
const obj = new TestObject();
obj.set('polygon', new Parse.Polygon(coords));
return obj
.save()
.then(() => {
const query = new Parse.Query(TestObject);
return query.get(obj.id);
})
.then(result => {
const polygon = result.get('polygon');
equal(polygon instanceof Parse.Polygon, true);
equal(polygon.coordinates, coords);
done();
}, done.fail);
});
it('polygon equalTo (open/closed) path', done => {
const openPoints = [[0, 0], [0, 1], [1, 1], [1, 0]];
const closedPoints = [[0, 0], [0, 1], [1, 1], [1, 0], [0, 0]];
const openPolygon = new Parse.Polygon(openPoints);
const closedPolygon = new Parse.Polygon(closedPoints);
const obj = new TestObject();
obj.set('polygon', openPolygon);
return obj
.save()
.then(() => {
const query = new Parse.Query(TestObject);
query.equalTo('polygon', openPolygon);
return query.find();
})
.then(results => {
const polygon = results[0].get('polygon');
equal(polygon instanceof Parse.Polygon, true);
equal(polygon.coordinates, closedPoints);
const query = new Parse.Query(TestObject);
query.equalTo('polygon', closedPolygon);
return query.find();
})
.then(results => {
const polygon = results[0].get('polygon');
equal(polygon instanceof Parse.Polygon, true);
equal(polygon.coordinates, closedPoints);
done();
}, done.fail);
});
it('polygon update', done => {
const oldCoords = [[0, 0], [0, 1], [1, 1], [1, 0]];
const oldPolygon = new Parse.Polygon(oldCoords);
const newCoords = [[2, 2], [2, 3], [3, 3], [3, 2]];
const newPolygon = new Parse.Polygon(newCoords);
const obj = new TestObject();
obj.set('polygon', oldPolygon);
return obj
.save()
.then(() => {
obj.set('polygon', newPolygon);
return obj.save();
})
.then(() => {
const query = new Parse.Query(TestObject);
return query.get(obj.id);
})
.then(result => {
const polygon = result.get('polygon');
newCoords.push(newCoords[0]);
equal(polygon instanceof Parse.Polygon, true);
equal(polygon.coordinates, newCoords);
done();
}, done.fail);
});
it('polygon invalid value', done => {
const coords = [['foo', 'bar'], [0, 1], [1, 0], [1, 1], [0, 0]];
const obj = new TestObject();
obj.set('polygon', { __type: 'Polygon', coordinates: coords });
return obj
.save()
.then(() => {
const query = new Parse.Query(TestObject);
return query.get(obj.id);
})
.then(done.fail, () => done());
});
it('polygon three points minimum', done => {
const coords = [[0, 0]];
const obj = new TestObject();
// use raw so we test the server validates properly
obj.set('polygon', { __type: 'Polygon', coordinates: coords });
obj.save().then(done.fail, () => done());
});
it('polygon three different points minimum', done => {
const coords = [[0, 0], [0, 1], [0, 0]];
const obj = new TestObject();
obj.set('polygon', new Parse.Polygon(coords));
obj.save().then(done.fail, () => done());
});
it('polygon counterclockwise', done => {
const coords = [[1, 1], [0, 1], [0, 0], [1, 0]];
const closed = [[1, 1], [0, 1], [0, 0], [1, 0], [1, 1]];
const obj = new TestObject();
obj.set('polygon', new Parse.Polygon(coords));
obj
.save()
.then(() => {
const query = new Parse.Query(TestObject);
return query.get(obj.id);
})
.then(result => {
const polygon = result.get('polygon');
equal(polygon instanceof Parse.Polygon, true);
equal(polygon.coordinates, closed);
done();
}, done.fail);
});
describe('with location', () => {
beforeAll(() => require('../lib/TestUtils').destroyAllDataPermanently());
it('polygonContain query', done => {
const points1 = [[0, 0], [0, 1], [1, 1], [1, 0]];
const points2 = [[0, 0], [0, 2], [2, 2], [2, 0]];
const points3 = [[10, 10], [10, 15], [15, 15], [15, 10], [10, 10]];
const polygon1 = new Parse.Polygon(points1);
const polygon2 = new Parse.Polygon(points2);
const polygon3 = new Parse.Polygon(points3);
const obj1 = new TestObject({ location: polygon1 });
const obj2 = new TestObject({ location: polygon2 });
const obj3 = new TestObject({ location: polygon3 });
Parse.Object.saveAll([obj1, obj2, obj3])
.then(() => {
const where = {
location: {
$geoIntersects: {
$point: { __type: 'GeoPoint', latitude: 0.5, longitude: 0.5 },
},
},
};
return request({
method: 'POST',
url: Parse.serverURL + '/classes/TestObject',
body: { where, _method: 'GET' },
headers: {
'X-Parse-Application-Id': Parse.applicationId,
'X-Parse-Javascript-Key': Parse.javaScriptKey,
'Content-Type': 'application/json',
},
});
})
.then(resp => {
expect(resp.data.results.length).toBe(2);
done();
}, done.fail);
});
it('polygonContain query no reverse input (Regression test for #4608)', done => {
const points1 = [[0.25, 0], [0.25, 1.25], [0.75, 1.25], [0.75, 0]];
const points2 = [[0, 0], [0, 2], [2, 2], [2, 0]];
const points3 = [[10, 10], [10, 15], [15, 15], [15, 10], [10, 10]];
const polygon1 = new Parse.Polygon(points1);
const polygon2 = new Parse.Polygon(points2);
const polygon3 = new Parse.Polygon(points3);
const obj1 = new TestObject({ location: polygon1 });
const obj2 = new TestObject({ location: polygon2 });
const obj3 = new TestObject({ location: polygon3 });
Parse.Object.saveAll([obj1, obj2, obj3])
.then(() => {
const where = {
location: {
$geoIntersects: {
$point: { __type: 'GeoPoint', latitude: 0.5, longitude: 1.0 },
},
},
};
return request({
method: 'POST',
url: Parse.serverURL + '/classes/TestObject',
body: { where, _method: 'GET' },
headers: {
'X-Parse-Application-Id': Parse.applicationId,
'X-Parse-Javascript-Key': Parse.javaScriptKey,
'Content-Type': 'application/json',
},
});
})
.then(resp => {
expect(resp.data.results.length).toBe(2);
done();
}, done.fail);
});
it('polygonContain query real data (Regression test for #4608)', done => {
const detroit = [
[42.631655189280224, -83.78406753121705],
[42.633047793854814, -83.75333640366955],
[42.61625254348911, -83.75149921669944],
[42.61526926650296, -83.78161794858735],
[42.631655189280224, -83.78406753121705],
];
const polygon = new Parse.Polygon(detroit);
const obj = new TestObject({ location: polygon });
obj
.save()
.then(() => {
const where = {
location: {
$geoIntersects: {
$point: {
__type: 'GeoPoint',
latitude: 42.624599,
longitude: -83.770162,
},
},
},
};
return request({
method: 'POST',
url: Parse.serverURL + '/classes/TestObject',
body: { where, _method: 'GET' },
headers: {
'X-Parse-Application-Id': Parse.applicationId,
'X-Parse-Javascript-Key': Parse.javaScriptKey,
'Content-Type': 'application/json',
},
});
})
.then(resp => {
expect(resp.data.results.length).toBe(1);
done();
}, done.fail);
});
it('polygonContain invalid input', done => {
const points = [[0, 0], [0, 1], [1, 1], [1, 0]];
const polygon = new Parse.Polygon(points);
const obj = new TestObject({ location: polygon });
obj
.save()
.then(() => {
const where = {
location: {
$geoIntersects: {
$point: { __type: 'GeoPoint', latitude: 181, longitude: 181 },
},
},
};
return request({
method: 'POST',
url: Parse.serverURL + '/classes/TestObject',
body: { where, _method: 'GET' },
headers: {
'X-Parse-Application-Id': Parse.applicationId,
'X-Parse-Javascript-Key': Parse.javaScriptKey,
},
});
})
.then(done.fail, () => done());
});
it('polygonContain invalid geoPoint', done => {
const points = [[0, 0], [0, 1], [1, 1], [1, 0]];
const polygon = new Parse.Polygon(points);
const obj = new TestObject({ location: polygon });
obj
.save()
.then(() => {
const where = {
location: {
$geoIntersects: {
$point: [],
},
},
};
return request({
method: 'POST',
url: Parse.serverURL + '/classes/TestObject',
body: { where, _method: 'GET' },
headers: {
'X-Parse-Application-Id': Parse.applicationId,
'X-Parse-Javascript-Key': Parse.javaScriptKey,
},
});
})
.then(done.fail, () => done());
});
});
});
describe_only_db('mongo')('Parse.Polygon testing', () => {
beforeEach(() => require('../lib/TestUtils').destroyAllDataPermanently());
it('support 2d and 2dsphere', done => {
const coords = [[0, 0], [0, 1], [1, 1], [1, 0], [0, 0]];
// testings against REST API, use raw formats
const polygon = { __type: 'Polygon', coordinates: coords };
const location = { __type: 'GeoPoint', latitude: 10, longitude: 10 };
const databaseAdapter = new MongoStorageAdapter({ uri: mongoURI });
return reconfigureServer({
appId: 'test',
restAPIKey: 'rest',
publicServerURL: 'http://localhost:8378/1',
databaseAdapter,
})
.then(() => {
return databaseAdapter.createIndex('TestObject', { location: '2d' });
})
.then(() => {
return databaseAdapter.createIndex('TestObject', {
polygon: '2dsphere',
});
})
.then(() => {
return request({
method: 'POST',
url: 'http://localhost:8378/1/classes/TestObject',
body: {
_method: 'POST',
location,
polygon,
polygon2: polygon,
},
headers: defaultHeaders,
});
})
.then(resp => {
return request({
method: 'POST',
url: `http://localhost:8378/1/classes/TestObject/${
resp.data.objectId
}`,
body: { _method: 'GET' },
headers: defaultHeaders,
});
})
.then(resp => {
equal(resp.data.location, location);
equal(resp.data.polygon, polygon);
equal(resp.data.polygon2, polygon);
return databaseAdapter.getIndexes('TestObject');
})
.then(indexes => {
equal(indexes.length, 4);
equal(indexes[0].key, { _id: 1 });
equal(indexes[1].key, { location: '2d' });
equal(indexes[2].key, { polygon: '2dsphere' });
equal(indexes[3].key, { polygon2: '2dsphere' });
done();
}, done.fail);
});
it('polygon coordinates reverse input', done => {
const Config = require('../lib/Config');
const config = Config.get('test');
// When stored the first point should be the last point
const input = [[12, 11], [14, 13], [16, 15], [18, 17]];
const output = [[[11, 12], [13, 14], [15, 16], [17, 18], [11, 12]]];
const obj = new TestObject();
obj.set('polygon', new Parse.Polygon(input));
obj
.save()
.then(() => {
return config.database.adapter._rawFind('TestObject', { _id: obj.id });
})
.then(results => {
expect(results.length).toBe(1);
expect(results[0].polygon.coordinates).toEqual(output);
done();
});
});
it('polygon loop is not valid', done => {
const coords = [[0, 0], [0, 1], [1, 0], [1, 1]];
const obj = new TestObject();
obj.set('polygon', new Parse.Polygon(coords));
obj.save().then(done.fail, () => done());
});
});
+67 -37
View File
@@ -1,80 +1,105 @@
var ParsePubSub = require('../src/LiveQuery/ParsePubSub').ParsePubSub;
const ParsePubSub = require('../lib/LiveQuery/ParsePubSub').ParsePubSub;
describe('ParsePubSub', function() {
beforeEach(function(done) {
// Mock RedisPubSub
var mockRedisPubSub = {
const mockRedisPubSub = {
createPublisher: jasmine.createSpy('createPublisherRedis'),
createSubscriber: jasmine.createSpy('createSubscriberRedis')
createSubscriber: jasmine.createSpy('createSubscriberRedis'),
};
jasmine.mockLibrary('../src/Adapters/PubSub/RedisPubSub', 'RedisPubSub', mockRedisPubSub);
jasmine.mockLibrary(
'../lib/Adapters/PubSub/RedisPubSub',
'RedisPubSub',
mockRedisPubSub
);
// Mock EventEmitterPubSub
var mockEventEmitterPubSub = {
const mockEventEmitterPubSub = {
createPublisher: jasmine.createSpy('createPublisherEventEmitter'),
createSubscriber: jasmine.createSpy('createSubscriberEventEmitter')
createSubscriber: jasmine.createSpy('createSubscriberEventEmitter'),
};
jasmine.mockLibrary('../src/Adapters/PubSub/EventEmitterPubSub', 'EventEmitterPubSub', mockEventEmitterPubSub);
jasmine.mockLibrary(
'../lib/Adapters/PubSub/EventEmitterPubSub',
'EventEmitterPubSub',
mockEventEmitterPubSub
);
done();
});
it('can create redis publisher', function() {
ParsePubSub.createPublisher({
redisURL: 'redisURL'
redisURL: 'redisURL',
redisOptions: { socket_keepalive: true },
});
var RedisPubSub = require('../src/Adapters/PubSub/RedisPubSub').RedisPubSub;
var EventEmitterPubSub = require('../src/Adapters/PubSub/EventEmitterPubSub').EventEmitterPubSub;
expect(RedisPubSub.createPublisher).toHaveBeenCalledWith({redisURL: 'redisURL'});
const RedisPubSub = require('../lib/Adapters/PubSub/RedisPubSub')
.RedisPubSub;
const EventEmitterPubSub = require('../lib/Adapters/PubSub/EventEmitterPubSub')
.EventEmitterPubSub;
expect(RedisPubSub.createPublisher).toHaveBeenCalledWith({
redisURL: 'redisURL',
redisOptions: { socket_keepalive: true },
});
expect(EventEmitterPubSub.createPublisher).not.toHaveBeenCalled();
});
it('can create event emitter publisher', function() {
ParsePubSub.createPublisher({});
var RedisPubSub = require('../src/Adapters/PubSub/RedisPubSub').RedisPubSub;
var EventEmitterPubSub = require('../src/Adapters/PubSub/EventEmitterPubSub').EventEmitterPubSub;
const RedisPubSub = require('../lib/Adapters/PubSub/RedisPubSub')
.RedisPubSub;
const EventEmitterPubSub = require('../lib/Adapters/PubSub/EventEmitterPubSub')
.EventEmitterPubSub;
expect(RedisPubSub.createPublisher).not.toHaveBeenCalled();
expect(EventEmitterPubSub.createPublisher).toHaveBeenCalled();
});
it('can create redis subscriber', function() {
ParsePubSub.createSubscriber({
redisURL: 'redisURL'
redisURL: 'redisURL',
redisOptions: { socket_keepalive: true },
});
var RedisPubSub = require('../src/Adapters/PubSub/RedisPubSub').RedisPubSub;
var EventEmitterPubSub = require('../src/Adapters/PubSub/EventEmitterPubSub').EventEmitterPubSub;
expect(RedisPubSub.createSubscriber).toHaveBeenCalledWith({redisURL: 'redisURL'});
const RedisPubSub = require('../lib/Adapters/PubSub/RedisPubSub')
.RedisPubSub;
const EventEmitterPubSub = require('../lib/Adapters/PubSub/EventEmitterPubSub')
.EventEmitterPubSub;
expect(RedisPubSub.createSubscriber).toHaveBeenCalledWith({
redisURL: 'redisURL',
redisOptions: { socket_keepalive: true },
});
expect(EventEmitterPubSub.createSubscriber).not.toHaveBeenCalled();
});
it('can create event emitter subscriber', function() {
ParsePubSub.createSubscriber({});
var RedisPubSub = require('../src/Adapters/PubSub/RedisPubSub').RedisPubSub;
var EventEmitterPubSub = require('../src/Adapters/PubSub/EventEmitterPubSub').EventEmitterPubSub;
const RedisPubSub = require('../lib/Adapters/PubSub/RedisPubSub')
.RedisPubSub;
const EventEmitterPubSub = require('../lib/Adapters/PubSub/EventEmitterPubSub')
.EventEmitterPubSub;
expect(RedisPubSub.createSubscriber).not.toHaveBeenCalled();
expect(EventEmitterPubSub.createSubscriber).toHaveBeenCalled();
});
it('can create publisher/sub with custom adapter', function() {
const adapter = {
const adapter = {
createPublisher: jasmine.createSpy('createPublisher'),
createSubscriber: jasmine.createSpy('createSubscriber')
}
createSubscriber: jasmine.createSpy('createSubscriber'),
};
ParsePubSub.createPublisher({
pubSubAdapter: adapter
pubSubAdapter: adapter,
});
expect(adapter.createPublisher).toHaveBeenCalled();
ParsePubSub.createSubscriber({
pubSubAdapter: adapter
pubSubAdapter: adapter,
});
expect(adapter.createSubscriber).toHaveBeenCalled();
var RedisPubSub = require('../src/Adapters/PubSub/RedisPubSub').RedisPubSub;
var EventEmitterPubSub = require('../src/Adapters/PubSub/EventEmitterPubSub').EventEmitterPubSub;
const RedisPubSub = require('../lib/Adapters/PubSub/RedisPubSub')
.RedisPubSub;
const EventEmitterPubSub = require('../lib/Adapters/PubSub/EventEmitterPubSub')
.EventEmitterPubSub;
expect(RedisPubSub.createSubscriber).not.toHaveBeenCalled();
expect(EventEmitterPubSub.createSubscriber).not.toHaveBeenCalled();
expect(RedisPubSub.createPublisher).not.toHaveBeenCalled();
@@ -82,34 +107,39 @@ describe('ParsePubSub', function() {
});
it('can create publisher/sub with custom function adapter', function() {
const adapter = {
const adapter = {
createPublisher: jasmine.createSpy('createPublisher'),
createSubscriber: jasmine.createSpy('createSubscriber')
}
createSubscriber: jasmine.createSpy('createSubscriber'),
};
ParsePubSub.createPublisher({
pubSubAdapter: function() {
return adapter;
}
},
});
expect(adapter.createPublisher).toHaveBeenCalled();
ParsePubSub.createSubscriber({
pubSubAdapter: function() {
return adapter;
}
},
});
expect(adapter.createSubscriber).toHaveBeenCalled();
var RedisPubSub = require('../src/Adapters/PubSub/RedisPubSub').RedisPubSub;
var EventEmitterPubSub = require('../src/Adapters/PubSub/EventEmitterPubSub').EventEmitterPubSub;
const RedisPubSub = require('../lib/Adapters/PubSub/RedisPubSub')
.RedisPubSub;
const EventEmitterPubSub = require('../lib/Adapters/PubSub/EventEmitterPubSub')
.EventEmitterPubSub;
expect(RedisPubSub.createSubscriber).not.toHaveBeenCalled();
expect(EventEmitterPubSub.createSubscriber).not.toHaveBeenCalled();
expect(RedisPubSub.createPublisher).not.toHaveBeenCalled();
expect(EventEmitterPubSub.createPublisher).not.toHaveBeenCalled();
});
afterEach(function(){
jasmine.restoreLibrary('../src/Adapters/PubSub/RedisPubSub', 'RedisPubSub');
jasmine.restoreLibrary('../src/Adapters/PubSub/EventEmitterPubSub', 'EventEmitterPubSub');
afterEach(function() {
jasmine.restoreLibrary('../lib/Adapters/PubSub/RedisPubSub', 'RedisPubSub');
jasmine.restoreLibrary(
'../lib/Adapters/PubSub/EventEmitterPubSub',
'EventEmitterPubSub'
);
});
});
File diff suppressed because it is too large Load Diff
+556 -400
View File
@@ -1,11 +1,15 @@
'use strict';
const MongoStorageAdapter = require('../src/Adapters/Storage/Mongo/MongoStorageAdapter');
const mongoURI = 'mongodb://localhost:27017/parseServerMongoAdapterTestDatabase';
const PostgresStorageAdapter = require('../src/Adapters/Storage/Postgres/PostgresStorageAdapter');
const postgresURI = 'postgres://localhost:5432/parse_server_postgres_adapter_test_database';
const MongoStorageAdapter = require('../lib/Adapters/Storage/Mongo/MongoStorageAdapter')
.default;
const mongoURI =
'mongodb://localhost:27017/parseServerMongoAdapterTestDatabase';
const PostgresStorageAdapter = require('../lib/Adapters/Storage/Postgres/PostgresStorageAdapter')
.default;
const postgresURI =
'postgres://localhost:5432/parse_server_postgres_adapter_test_database';
const Parse = require('parse/node');
const rp = require('request-promise');
const request = require('../lib/request');
let databaseAdapter;
const fullTextHelper = () => {
@@ -29,11 +33,12 @@ const fullTextHelper = () => {
const requests = [];
for (const i in subjects) {
const request = {
method: "POST",
method: 'POST',
body: {
subject: subjects[i]
subject: subjects[i],
comment: subjects[i],
},
path: "/1/classes/TestObject"
path: '/1/classes/TestObject',
};
requests.push(request);
}
@@ -41,419 +46,570 @@ const fullTextHelper = () => {
appId: 'test',
restAPIKey: 'test',
publicServerURL: 'http://localhost:8378/1',
databaseAdapter
databaseAdapter,
}).then(() => {
if (process.env.PARSE_SERVER_TEST_DB === 'postgres') {
return Parse.Promise.as();
}
return databaseAdapter.createIndex('TestObject', {subject: 'text'});
}).then(() => {
return rp.post({
return request({
method: 'POST',
url: 'http://localhost:8378/1/batch',
body: {
requests
requests,
},
json: true,
headers: {
'X-Parse-Application-Id': 'test',
'X-Parse-REST-API-Key': 'test'
}
'X-Parse-REST-API-Key': 'test',
'Content-Type': 'application/json',
},
});
});
}
};
describe('Parse.Query Full Text Search testing', () => {
it('fullTextSearch: $search', (done) => {
fullTextHelper().then(() => {
const where = {
subject: {
$text: {
$search: {
$term: 'coffee'
}
}
}
};
return rp.post({
url: 'http://localhost:8378/1/classes/TestObject',
json: { where, '_method': 'GET' },
headers: {
'X-Parse-Application-Id': 'test',
'X-Parse-REST-API-Key': 'test'
}
});
}).then((resp) => {
expect(resp.results.length).toBe(3);
done();
}, done.fail);
});
it('fullTextSearch: $search, sort', (done) => {
fullTextHelper().then(() => {
const where = {
subject: {
$text: {
$search: {
$term: 'coffee'
}
}
}
};
const order = '$score';
const keys = '$score';
return rp.post({
url: 'http://localhost:8378/1/classes/TestObject',
json: { where, order, keys, '_method': 'GET' },
headers: {
'X-Parse-Application-Id': 'test',
'X-Parse-REST-API-Key': 'test'
}
});
}).then((resp) => {
expect(resp.results.length).toBe(3);
expect(resp.results[0].score);
expect(resp.results[1].score);
expect(resp.results[2].score);
done();
}, done.fail);
});
it('fullTextSearch: $language', (done) => {
fullTextHelper().then(() => {
const where = {
subject: {
$text: {
$search: {
$term: 'leche',
$language: 'spanish'
}
}
}
};
return rp.post({
url: 'http://localhost:8378/1/classes/TestObject',
json: { where, '_method': 'GET' },
headers: {
'X-Parse-Application-Id': 'test',
'X-Parse-REST-API-Key': 'test'
}
});
}).then((resp) => {
expect(resp.results.length).toBe(2);
done();
}, done.fail);
});
it('fullTextSearch: $diacriticSensitive', (done) => {
fullTextHelper().then(() => {
const where = {
subject: {
$text: {
$search: {
$term: 'CAFÉ',
$diacriticSensitive: true
}
}
}
};
return rp.post({
url: 'http://localhost:8378/1/classes/TestObject',
json: { where, '_method': 'GET' },
headers: {
'X-Parse-Application-Id': 'test',
'X-Parse-REST-API-Key': 'test'
}
});
}).then((resp) => {
expect(resp.results.length).toBe(1);
done();
}, done.fail);
});
it('fullTextSearch: $search, invalid input', (done) => {
fullTextHelper().then(() => {
const where = {
subject: {
$text: {
$search: true
}
}
};
return rp.post({
url: 'http://localhost:8378/1/classes/TestObject',
json: { where, '_method': 'GET' },
headers: {
'X-Parse-Application-Id': 'test',
'X-Parse-REST-API-Key': 'test'
}
});
}).then((resp) => {
fail(`no request should succeed: ${JSON.stringify(resp)}`);
done();
}).catch((err) => {
expect(err.error.code).toEqual(Parse.Error.INVALID_JSON);
done();
});
});
it('fullTextSearch: $language, invalid input', (done) => {
fullTextHelper().then(() => {
const where = {
subject: {
$text: {
$search: {
$term: 'leche',
$language: true
}
}
}
};
return rp.post({
url: 'http://localhost:8378/1/classes/TestObject',
json: { where, '_method': 'GET' },
headers: {
'X-Parse-Application-Id': 'test',
'X-Parse-REST-API-Key': 'test'
}
});
}).then((resp) => {
fail(`no request should succeed: ${JSON.stringify(resp)}`);
done();
}).catch((err) => {
expect(err.error.code).toEqual(Parse.Error.INVALID_JSON);
done();
});
});
it('fullTextSearch: $caseSensitive, invalid input', (done) => {
fullTextHelper().then(() => {
const where = {
subject: {
$text: {
$search: {
$term: 'Coffee',
$caseSensitive: 'string'
}
}
}
};
return rp.post({
url: 'http://localhost:8378/1/classes/TestObject',
json: { where, '_method': 'GET' },
headers: {
'X-Parse-Application-Id': 'test',
'X-Parse-REST-API-Key': 'test'
}
});
}).then((resp) => {
fail(`no request should succeed: ${JSON.stringify(resp)}`);
done();
}).catch((err) => {
expect(err.error.code).toEqual(Parse.Error.INVALID_JSON);
done();
});
});
it('fullTextSearch: $diacriticSensitive, invalid input', (done) => {
fullTextHelper().then(() => {
const where = {
subject: {
$text: {
$search: {
$term: 'CAFÉ',
$diacriticSensitive: 'string'
}
}
}
};
return rp.post({
url: 'http://localhost:8378/1/classes/TestObject',
json: { where, '_method': 'GET' },
headers: {
'X-Parse-Application-Id': 'test',
'X-Parse-REST-API-Key': 'test'
}
});
}).then((resp) => {
fail(`no request should succeed: ${JSON.stringify(resp)}`);
done();
}).catch((err) => {
expect(err.error.code).toEqual(Parse.Error.INVALID_JSON);
done();
});
});
});
describe_only_db('mongo')('Parse.Query Full Text Search testing', () => {
it('fullTextSearch: $search, index not exist', (done) => {
return reconfigureServer({
appId: 'test',
restAPIKey: 'test',
publicServerURL: 'http://localhost:8378/1',
databaseAdapter: new MongoStorageAdapter({ uri: mongoURI })
}).then(() => {
return rp.post({
url: 'http://localhost:8378/1/batch',
body: {
requests: [
{
method: "POST",
body: {
subject: "coffee is java"
it('fullTextSearch: $search', done => {
fullTextHelper()
.then(() => {
const where = {
subject: {
$text: {
$search: {
$term: 'coffee',
},
path: "/1/classes/TestObject"
},
{
method: "POST",
body: {
subject: "java is coffee"
},
path: "/1/classes/TestObject"
}
]
},
};
return request({
method: 'POST',
url: 'http://localhost:8378/1/classes/TestObject',
body: { where, _method: 'GET' },
headers: {
'X-Parse-Application-Id': 'test',
'X-Parse-REST-API-Key': 'test',
'Content-Type': 'application/json',
},
});
})
.then(
resp => {
expect(resp.data.results.length).toBe(3);
done();
},
json: true,
headers: {
'X-Parse-Application-Id': 'test',
'X-Parse-REST-API-Key': 'test'
}
});
}).then(() => {
const where = {
subject: {
$text: {
$search: {
$term: 'coffee'
}
}
}
};
return rp.post({
url: 'http://localhost:8378/1/classes/TestObject',
json: { where, '_method': 'GET' },
headers: {
'X-Parse-Application-Id': 'test',
'X-Parse-REST-API-Key': 'test'
}
});
}).then((resp) => {
fail(`Text Index should not exist: ${JSON.stringify(resp)}`);
done();
}).catch((err) => {
expect(err.error.code).toEqual(Parse.Error.INTERNAL_SERVER_ERROR);
done();
});
e => done.fail(e)
);
});
it('fullTextSearch: $diacriticSensitive - false', (done) => {
fullTextHelper().then(() => {
const where = {
subject: {
$text: {
$search: {
$term: 'CAFÉ',
$diacriticSensitive: false
}
}
}
};
return rp.post({
url: 'http://localhost:8378/1/classes/TestObject',
json: { where, '_method': 'GET' },
headers: {
'X-Parse-Application-Id': 'test',
'X-Parse-REST-API-Key': 'test'
}
});
}).then((resp) => {
expect(resp.results.length).toBe(2);
done();
}, done.fail);
it('fullTextSearch: $search, sort', done => {
fullTextHelper()
.then(() => {
const where = {
subject: {
$text: {
$search: {
$term: 'coffee',
},
},
},
};
const order = '$score';
const keys = '$score';
return request({
method: 'POST',
url: 'http://localhost:8378/1/classes/TestObject',
body: { where, order, keys, _method: 'GET' },
headers: {
'X-Parse-Application-Id': 'test',
'X-Parse-REST-API-Key': 'test',
'Content-Type': 'application/json',
},
});
})
.then(response => {
const resp = response.data;
expect(resp.results.length).toBe(3);
expect(resp.results[0].score);
expect(resp.results[1].score);
expect(resp.results[2].score);
done();
}, done.fail);
});
it('fullTextSearch: $caseSensitive', (done) => {
fullTextHelper().then(() => {
const where = {
subject: {
$text: {
$search: {
$term: 'Coffee',
$caseSensitive: true
}
}
}
};
return rp.post({
url: 'http://localhost:8378/1/classes/TestObject',
json: { where, '_method': 'GET' },
headers: {
'X-Parse-Application-Id': 'test',
'X-Parse-REST-API-Key': 'test'
}
it('fullTextSearch: $language', done => {
fullTextHelper()
.then(() => {
const where = {
subject: {
$text: {
$search: {
$term: 'leche',
$language: 'spanish',
},
},
},
};
return request({
method: 'POST',
url: 'http://localhost:8378/1/classes/TestObject',
body: { where, _method: 'GET' },
headers: {
'X-Parse-Application-Id': 'test',
'X-Parse-REST-API-Key': 'test',
'Content-Type': 'application/json',
},
});
})
.then(resp => {
expect(resp.data.results.length).toBe(2);
done();
}, done.fail);
});
it('fullTextSearch: $diacriticSensitive', done => {
fullTextHelper()
.then(() => {
const where = {
subject: {
$text: {
$search: {
$term: 'CAFÉ',
$diacriticSensitive: true,
},
},
},
};
return request({
method: 'POST',
url: 'http://localhost:8378/1/classes/TestObject',
body: { where, _method: 'GET' },
headers: {
'X-Parse-Application-Id': 'test',
'X-Parse-REST-API-Key': 'test',
'Content-Type': 'application/json',
},
});
})
.then(resp => {
expect(resp.data.results.length).toBe(1);
done();
}, done.fail);
});
it('fullTextSearch: $search, invalid input', done => {
fullTextHelper()
.then(() => {
const where = {
subject: {
$text: {
$search: true,
},
},
};
return request({
method: 'POST',
url: 'http://localhost:8378/1/classes/TestObject',
body: { where, _method: 'GET' },
headers: {
'X-Parse-Application-Id': 'test',
'X-Parse-REST-API-Key': 'test',
'Content-Type': 'application/json',
},
});
})
.then(resp => {
fail(`no request should succeed: ${JSON.stringify(resp)}`);
done();
})
.catch(err => {
expect(err.data.code).toEqual(Parse.Error.INVALID_JSON);
done();
});
});
it('fullTextSearch: $language, invalid input', done => {
fullTextHelper()
.then(() => {
const where = {
subject: {
$text: {
$search: {
$term: 'leche',
$language: true,
},
},
},
};
return request({
method: 'POST',
url: 'http://localhost:8378/1/classes/TestObject',
body: { where, _method: 'GET' },
headers: {
'X-Parse-Application-Id': 'test',
'X-Parse-REST-API-Key': 'test',
'Content-Type': 'application/json',
},
});
})
.then(resp => {
fail(`no request should succeed: ${JSON.stringify(resp)}`);
done();
})
.catch(err => {
expect(err.data.code).toEqual(Parse.Error.INVALID_JSON);
done();
});
});
it('fullTextSearch: $caseSensitive, invalid input', done => {
fullTextHelper()
.then(() => {
const where = {
subject: {
$text: {
$search: {
$term: 'Coffee',
$caseSensitive: 'string',
},
},
},
};
return request({
method: 'POST',
url: 'http://localhost:8378/1/classes/TestObject',
body: { where, _method: 'GET' },
headers: {
'X-Parse-Application-Id': 'test',
'X-Parse-REST-API-Key': 'test',
'Content-Type': 'application/json',
},
});
})
.then(resp => {
fail(`no request should succeed: ${JSON.stringify(resp)}`);
done();
})
.catch(err => {
expect(err.data.code).toEqual(Parse.Error.INVALID_JSON);
done();
});
});
it('fullTextSearch: $diacriticSensitive, invalid input', done => {
fullTextHelper()
.then(() => {
const where = {
subject: {
$text: {
$search: {
$term: 'CAFÉ',
$diacriticSensitive: 'string',
},
},
},
};
return request({
method: 'POST',
url: 'http://localhost:8378/1/classes/TestObject',
body: { where, _method: 'GET' },
headers: {
'X-Parse-Application-Id': 'test',
'X-Parse-REST-API-Key': 'test',
'Content-Type': 'application/json',
},
});
})
.then(resp => {
fail(`no request should succeed: ${JSON.stringify(resp)}`);
done();
})
.catch(err => {
expect(err.data.code).toEqual(Parse.Error.INVALID_JSON);
done();
});
}).then((resp) => {
expect(resp.results.length).toBe(1);
done();
}, done.fail);
});
});
describe_only_db('postgres')('Parse.Query Full Text Search testing', () => {
it('fullTextSearch: $diacriticSensitive - false', (done) => {
fullTextHelper().then(() => {
const where = {
subject: {
$text: {
$search: {
$term: 'CAFÉ',
$diacriticSensitive: false
}
}
}
};
return rp.post({
url: 'http://localhost:8378/1/classes/TestObject',
json: { where, '_method': 'GET' },
headers: {
'X-Parse-Application-Id': 'test',
'X-Parse-REST-API-Key': 'test'
}
});
}).then((resp) => {
fail(`$diacriticSensitive - false should not supported: ${JSON.stringify(resp)}`);
done();
}).catch((err) => {
expect(err.error.code).toEqual(Parse.Error.INVALID_JSON);
done();
describe_only_db('mongo')(
'[mongodb] Parse.Query Full Text Search testing',
() => {
it('fullTextSearch: does not create text index if compound index exist', done => {
fullTextHelper()
.then(() => {
return databaseAdapter.dropAllIndexes('TestObject');
})
.then(() => {
return databaseAdapter.getIndexes('TestObject');
})
.then(indexes => {
expect(indexes.length).toEqual(1);
return databaseAdapter.createIndex('TestObject', {
subject: 'text',
comment: 'text',
});
})
.then(() => {
return databaseAdapter.getIndexes('TestObject');
})
.then(indexes => {
expect(indexes.length).toEqual(2);
const where = {
subject: {
$text: {
$search: {
$term: 'coffee',
},
},
},
};
return request({
method: 'POST',
url: 'http://localhost:8378/1/classes/TestObject',
body: { where, _method: 'GET' },
headers: {
'X-Parse-Application-Id': 'test',
'X-Parse-REST-API-Key': 'test',
'Content-Type': 'application/json',
},
});
})
.then(resp => {
expect(resp.data.results.length).toEqual(3);
return databaseAdapter.getIndexes('TestObject');
})
.then(indexes => {
expect(indexes.length).toEqual(2);
request({
url: 'http://localhost:8378/1/schemas/TestObject',
headers: {
'X-Parse-Application-Id': 'test',
'X-Parse-Master-Key': 'test',
'Content-Type': 'application/json',
},
}).then(response => {
const body = response.data;
expect(body.indexes._id_).toBeDefined();
expect(body.indexes._id_._id).toEqual(1);
expect(body.indexes.subject_text_comment_text).toBeDefined();
expect(body.indexes.subject_text_comment_text.subject).toEqual(
'text'
);
expect(body.indexes.subject_text_comment_text.comment).toEqual(
'text'
);
done();
});
})
.catch(done.fail);
});
});
it('fullTextSearch: $caseSensitive', (done) => {
fullTextHelper().then(() => {
const where = {
subject: {
$text: {
$search: {
$term: 'Coffee',
$caseSensitive: true
}
}
}
};
return rp.post({
url: 'http://localhost:8378/1/classes/TestObject',
json: { where, '_method': 'GET' },
headers: {
'X-Parse-Application-Id': 'test',
'X-Parse-REST-API-Key': 'test'
}
});
}).then((resp) => {
fail(`$caseSensitive should not supported: ${JSON.stringify(resp)}`);
done();
}).catch((err) => {
expect(err.error.code).toEqual(Parse.Error.INVALID_JSON);
done();
it('fullTextSearch: does not create text index if schema compound index exist', done => {
fullTextHelper()
.then(() => {
return databaseAdapter.dropAllIndexes('TestObject');
})
.then(() => {
return databaseAdapter.getIndexes('TestObject');
})
.then(indexes => {
expect(indexes.length).toEqual(1);
return request({
method: 'PUT',
url: 'http://localhost:8378/1/schemas/TestObject',
headers: {
'X-Parse-Application-Id': 'test',
'X-Parse-REST-API-Key': 'test',
'X-Parse-Master-Key': 'test',
'Content-Type': 'application/json',
},
body: {
indexes: {
text_test: { subject: 'text', comment: 'text' },
},
},
});
})
.then(() => {
return databaseAdapter.getIndexes('TestObject');
})
.then(indexes => {
expect(indexes.length).toEqual(2);
const where = {
subject: {
$text: {
$search: {
$term: 'coffee',
},
},
},
};
return request({
method: 'POST',
url: 'http://localhost:8378/1/classes/TestObject',
body: { where, _method: 'GET' },
headers: {
'X-Parse-Application-Id': 'test',
'X-Parse-REST-API-Key': 'test',
'Content-Type': 'application/json',
},
});
})
.then(resp => {
expect(resp.data.results.length).toEqual(3);
return databaseAdapter.getIndexes('TestObject');
})
.then(indexes => {
expect(indexes.length).toEqual(2);
request({
url: 'http://localhost:8378/1/schemas/TestObject',
headers: {
'X-Parse-Application-Id': 'test',
'X-Parse-Master-Key': 'test',
'Content-Type': 'application/json',
},
}).then(response => {
const body = response.data;
expect(body.indexes._id_).toBeDefined();
expect(body.indexes._id_._id).toEqual(1);
expect(body.indexes.text_test).toBeDefined();
expect(body.indexes.text_test.subject).toEqual('text');
expect(body.indexes.text_test.comment).toEqual('text');
done();
});
})
.catch(done.fail);
});
});
});
it('fullTextSearch: $diacriticSensitive - false', done => {
fullTextHelper()
.then(() => {
const where = {
subject: {
$text: {
$search: {
$term: 'CAFÉ',
$diacriticSensitive: false,
},
},
},
};
return request({
method: 'POST',
url: 'http://localhost:8378/1/classes/TestObject',
body: { where, _method: 'GET' },
headers: {
'X-Parse-Application-Id': 'test',
'X-Parse-REST-API-Key': 'test',
'Content-Type': 'application/json',
},
});
})
.then(resp => {
expect(resp.data.results.length).toBe(2);
done();
}, done.fail);
});
it('fullTextSearch: $caseSensitive', done => {
fullTextHelper()
.then(() => {
const where = {
subject: {
$text: {
$search: {
$term: 'Coffee',
$caseSensitive: true,
},
},
},
};
return request({
method: 'POST',
url: 'http://localhost:8378/1/classes/TestObject',
body: { where, _method: 'GET' },
headers: {
'X-Parse-Application-Id': 'test',
'X-Parse-REST-API-Key': 'test',
'Content-Type': 'application/json',
},
});
})
.then(resp => {
expect(resp.data.results.length).toBe(1);
done();
}, done.fail);
});
}
);
describe_only_db('postgres')(
'[postgres] Parse.Query Full Text Search testing',
() => {
it('fullTextSearch: $diacriticSensitive - false', done => {
fullTextHelper()
.then(() => {
const where = {
subject: {
$text: {
$search: {
$term: 'CAFÉ',
$diacriticSensitive: false,
},
},
},
};
return request({
method: 'POST',
url: 'http://localhost:8378/1/classes/TestObject',
body: { where, _method: 'GET' },
headers: {
'X-Parse-Application-Id': 'test',
'X-Parse-REST-API-Key': 'test',
'Content-Type': 'application/json',
},
});
})
.then(resp => {
fail(
`$diacriticSensitive - false should not supported: ${JSON.stringify(
resp
)}`
);
done();
})
.catch(err => {
expect(err.data.code).toEqual(Parse.Error.INVALID_JSON);
done();
});
});
it('fullTextSearch: $caseSensitive', done => {
fullTextHelper()
.then(() => {
const where = {
subject: {
$text: {
$search: {
$term: 'Coffee',
$caseSensitive: true,
},
},
},
};
return request({
method: 'POST',
url: 'http://localhost:8378/1/classes/TestObject',
body: { where, _method: 'GET' },
headers: {
'X-Parse-Application-Id': 'test',
'X-Parse-REST-API-Key': 'test',
'Content-Type': 'application/json',
},
});
})
.then(resp => {
fail(`$caseSensitive should not supported: ${JSON.stringify(resp)}`);
done();
})
.catch(err => {
expect(err.data.code).toEqual(Parse.Error.INVALID_JSON);
done();
});
});
}
);
+4059 -2344
View File
File diff suppressed because it is too large Load Diff
+689 -592
View File
File diff suppressed because it is too large Load Diff
+540 -450
View File
File diff suppressed because it is too large Load Diff
+101
View File
@@ -0,0 +1,101 @@
'use strict';
/* Tests for ParseServer.js */
const express = require('express');
const MongoStorageAdapter = require('../lib/Adapters/Storage/Mongo/MongoStorageAdapter')
.default;
const PostgresStorageAdapter = require('../lib/Adapters/Storage/Postgres/PostgresStorageAdapter')
.default;
const ParseServer = require('../lib/ParseServer').default;
const path = require('path');
const { spawn } = require('child_process');
describe('Server Url Checks', () => {
let server;
beforeAll(done => {
const app = express();
app.get('/health', function(req, res) {
res.json({
status: 'ok',
});
});
server = app.listen(13376, undefined, done);
});
afterAll(done => {
server.close(done);
});
it('validate good server url', done => {
Parse.serverURL = 'http://localhost:13376';
ParseServer.verifyServerUrl(function(result) {
if (!result) {
done.fail('Did not pass valid url');
}
done();
});
});
it('mark bad server url', done => {
Parse.serverURL = 'notavalidurl';
ParseServer.verifyServerUrl(function(result) {
if (result) {
done.fail('Did not mark invalid url');
}
done();
});
});
it('handleShutdown, close connection', done => {
const mongoURI =
'mongodb://localhost:27017/parseServerMongoAdapterTestDatabase';
const postgresURI =
'postgres://localhost:5432/parse_server_postgres_adapter_test_database';
let databaseAdapter;
if (process.env.PARSE_SERVER_TEST_DB === 'postgres') {
databaseAdapter = new PostgresStorageAdapter({
uri: process.env.PARSE_SERVER_TEST_DATABASE_URI || postgresURI,
collectionPrefix: 'test_',
});
} else {
databaseAdapter = new MongoStorageAdapter({
uri: mongoURI,
collectionPrefix: 'test_',
});
}
const newConfiguration = Object.assign({}, defaultConfiguration, {
databaseAdapter,
serverStartComplete: () => {
parseServer.handleShutdown();
parseServer.server.close(err => {
if (err) {
done.fail('Close Server Error');
}
reconfigureServer({}).then(() => {
done();
});
});
},
});
const parseServer = ParseServer.start(newConfiguration);
});
it('does not have unhandled promise rejection in the case of load error', done => {
const parseServerProcess = spawn(
path.resolve(__dirname, './support/FailingServer.js')
);
let stdout;
let stderr;
parseServerProcess.stdout.on('data', data => {
stdout = data.toString();
});
parseServerProcess.stderr.on('data', data => {
stderr = data.toString();
});
parseServerProcess.on('close', code => {
expect(code).toEqual(1);
expect(stdout).toBeUndefined();
expect(stderr).toContain('MongoNetworkError');
done();
});
});
});
+184 -122
View File
@@ -1,155 +1,217 @@
const ParseServerRESTController = require('../src/ParseServerRESTController').ParseServerRESTController;
const ParseServer = require('../src/ParseServer').default;
const ParseServerRESTController = require('../lib/ParseServerRESTController')
.ParseServerRESTController;
const ParseServer = require('../lib/ParseServer').default;
const Parse = require('parse/node').Parse;
let RESTController;
describe('ParseServerRESTController', () => {
beforeEach(() => {
RESTController = ParseServerRESTController(Parse.applicationId, ParseServer.promiseRouter({appId: Parse.applicationId}));
})
it('should handle a get request', (done) => {
RESTController.request("GET", "/classes/MyObject").then((res) => {
expect(res.results.length).toBe(0);
done();
}, (err) => {
console.log(err);
jfail(err);
done();
});
RESTController = ParseServerRESTController(
Parse.applicationId,
ParseServer.promiseRouter({ appId: Parse.applicationId })
);
});
it('should handle a get request with full serverURL mount path', (done) => {
RESTController.request("GET", "/1/classes/MyObject").then((res) => {
expect(res.results.length).toBe(0);
done();
}, (err) => {
jfail(err);
done();
});
it('should handle a get request', done => {
RESTController.request('GET', '/classes/MyObject').then(
res => {
expect(res.results.length).toBe(0);
done();
},
err => {
console.log(err);
jfail(err);
done();
}
);
});
it('should handle a POST batch', (done) => {
RESTController.request("POST", "batch", {
it('should handle a get request with full serverURL mount path', done => {
RESTController.request('GET', '/1/classes/MyObject').then(
res => {
expect(res.results.length).toBe(0);
done();
},
err => {
jfail(err);
done();
}
);
});
it('should handle a POST batch', done => {
RESTController.request('POST', 'batch', {
requests: [
{
method: 'GET',
path: '/classes/MyObject'
path: '/classes/MyObject',
},
{
method: 'POST',
path: '/classes/MyObject',
body: {"key": "value"}
body: { key: 'value' },
},
{
method: 'GET',
path: '/classes/MyObject'
}
]
}).then((res) => {
expect(res.length).toBe(3);
done();
}, (err) => {
jfail(err);
done();
});
path: '/classes/MyObject',
},
],
}).then(
res => {
expect(res.length).toBe(3);
done();
},
err => {
jfail(err);
done();
}
);
});
it('should handle a POST request', (done) => {
RESTController.request("POST", "/classes/MyObject", {"key": "value"}).then(() => {
return RESTController.request("GET", "/classes/MyObject");
}).then((res) => {
expect(res.results.length).toBe(1);
expect(res.results[0].key).toEqual("value");
done();
}).fail((err) => {
console.log(err);
jfail(err);
done();
});
});
it('ensures sessionTokens are properly handled', (done) => {
let userId;
Parse.User.signUp('user', 'pass').then((user) => {
userId = user.id;
const sessionToken = user.getSessionToken();
return RESTController.request("GET", "/users/me", undefined, {sessionToken});
}).then((res) => {
// Result is in JSON format
expect(res.objectId).toEqual(userId);
done();
}).fail((err) => {
console.log(err);
jfail(err);
done();
});
});
it('ensures masterKey is properly handled', (done) => {
let userId;
Parse.User.signUp('user', 'pass').then((user) => {
userId = user.id;
return Parse.User.logOut().then(() => {
return RESTController.request("GET", "/classes/_User", undefined, {useMasterKey: true});
it('should handle a POST request', done => {
RESTController.request('POST', '/classes/MyObject', { key: 'value' })
.then(() => {
return RESTController.request('GET', '/classes/MyObject');
})
.then(res => {
expect(res.results.length).toBe(1);
expect(res.results[0].key).toEqual('value');
done();
})
.catch(err => {
console.log(err);
jfail(err);
done();
});
}).then((res) => {
expect(res.results.length).toBe(1);
expect(res.results[0].objectId).toEqual(userId);
done();
}, (err) => {
jfail(err);
done();
});
});
it('ensures no user is created when passing an empty username', (done) => {
RESTController.request("POST", "/classes/_User", {username: "", password: "world"}).then(() => {
jfail(new Error('Success callback should not be called when passing an empty username.'));
done();
}, (err) => {
expect(err.code).toBe(Parse.Error.USERNAME_MISSING);
expect(err.message).toBe('bad or missing username');
done();
});
it('ensures sessionTokens are properly handled', done => {
let userId;
Parse.User.signUp('user', 'pass')
.then(user => {
userId = user.id;
const sessionToken = user.getSessionToken();
return RESTController.request('GET', '/users/me', undefined, {
sessionToken,
});
})
.then(res => {
// Result is in JSON format
expect(res.objectId).toEqual(userId);
done();
})
.catch(err => {
console.log(err);
jfail(err);
done();
});
});
it('ensures no user is created when passing an empty password', (done) => {
RESTController.request("POST", "/classes/_User", {username: "hello", password: ""}).then(() => {
jfail(new Error('Success callback should not be called when passing an empty password.'));
done();
}, (err) => {
expect(err.code).toBe(Parse.Error.PASSWORD_MISSING);
expect(err.message).toBe('password is required');
done();
});
it('ensures masterKey is properly handled', done => {
let userId;
Parse.User.signUp('user', 'pass')
.then(user => {
userId = user.id;
return Parse.User.logOut().then(() => {
return RESTController.request('GET', '/classes/_User', undefined, {
useMasterKey: true,
});
});
})
.then(
res => {
expect(res.results.length).toBe(1);
expect(res.results[0].objectId).toEqual(userId);
done();
},
err => {
jfail(err);
done();
}
);
});
it('ensures no session token is created on creating users', (done) => {
RESTController.request("POST", "/classes/_User", {username: "hello", password: "world"}).then((user) => {
expect(user.sessionToken).toBeUndefined();
const query = new Parse.Query('_Session');
return query.find({useMasterKey: true});
}).then(sessions => {
expect(sessions.length).toBe(0);
done();
}, done.fail);
it('ensures no user is created when passing an empty username', done => {
RESTController.request('POST', '/classes/_User', {
username: '',
password: 'world',
}).then(
() => {
jfail(
new Error(
'Success callback should not be called when passing an empty username.'
)
);
done();
},
err => {
expect(err.code).toBe(Parse.Error.USERNAME_MISSING);
expect(err.message).toBe('bad or missing username');
done();
}
);
});
it('ensures a session token is created when passing installationId != cloud', (done) => {
RESTController.request("POST", "/classes/_User", {username: "hello", password: "world"}, {installationId: 'my-installation'}).then((user) => {
expect(user.sessionToken).not.toBeUndefined();
const query = new Parse.Query('_Session');
return query.find({useMasterKey: true});
}).then(sessions => {
expect(sessions.length).toBe(1);
expect(sessions[0].get('installationId')).toBe('my-installation');
done();
}, (err) => {
jfail(err);
done();
});
it('ensures no user is created when passing an empty password', done => {
RESTController.request('POST', '/classes/_User', {
username: 'hello',
password: '',
}).then(
() => {
jfail(
new Error(
'Success callback should not be called when passing an empty password.'
)
);
done();
},
err => {
expect(err.code).toBe(Parse.Error.PASSWORD_MISSING);
expect(err.message).toBe('password is required');
done();
}
);
});
it('ensures no session token is created on creating users', done => {
RESTController.request('POST', '/classes/_User', {
username: 'hello',
password: 'world',
})
.then(user => {
expect(user.sessionToken).toBeUndefined();
const query = new Parse.Query('_Session');
return query.find({ useMasterKey: true });
})
.then(sessions => {
expect(sessions.length).toBe(0);
done();
}, done.fail);
});
it('ensures a session token is created when passing installationId != cloud', done => {
RESTController.request(
'POST',
'/classes/_User',
{ username: 'hello', password: 'world' },
{ installationId: 'my-installation' }
)
.then(user => {
expect(user.sessionToken).not.toBeUndefined();
const query = new Parse.Query('_Session');
return query.find({ useMasterKey: true });
})
.then(
sessions => {
expect(sessions.length).toBe(1);
expect(sessions[0].get('installationId')).toBe('my-installation');
done();
},
err => {
jfail(err);
done();
}
);
});
});
+138
View File
@@ -0,0 +1,138 @@
//
// Tests behavior of Parse Sessions
//
'use strict';
function setupTestUsers() {
const user1 = new Parse.User();
const user2 = new Parse.User();
const user3 = new Parse.User();
user1.set('username', 'testuser_1');
user2.set('username', 'testuser_2');
user3.set('username', 'testuser_3');
user1.set('password', 'password');
user2.set('password', 'password');
user3.set('password', 'password');
return user1
.signUp()
.then(() => {
return user2.signUp();
})
.then(() => {
return user3.signUp();
});
}
describe('Parse.Session', () => {
// multiple sessions with masterKey + sessionToken
it('should retain original sessionTokens with masterKey & sessionToken set', done => {
setupTestUsers()
.then(user => {
const query = new Parse.Query(Parse.Session);
return query.find({
useMasterKey: true,
sessionToken: user.get('sessionToken'),
});
})
.then(results => {
const foundKeys = [];
expect(results.length).toBe(3);
for (const key in results) {
const sessionToken = results[key].get('sessionToken');
if (foundKeys[sessionToken]) {
fail('Duplicate session token present in response');
break;
}
foundKeys[sessionToken] = 1;
}
done();
})
.catch(err => {
fail(err);
});
});
// single session returned, with just one sessionToken
it('should retain original sessionTokens with just sessionToken set', done => {
let knownSessionToken;
setupTestUsers()
.then(user => {
knownSessionToken = user.get('sessionToken');
const query = new Parse.Query(Parse.Session);
return query.find({
sessionToken: knownSessionToken,
});
})
.then(results => {
expect(results.length).toBe(1);
const sessionToken = results[0].get('sessionToken');
expect(sessionToken).toBe(knownSessionToken);
done();
})
.catch(err => {
fail(err);
});
});
// multiple users with masterKey + sessionToken
it('token on users should retain original sessionTokens with masterKey & sessionToken set', done => {
setupTestUsers()
.then(user => {
const query = new Parse.Query(Parse.User);
return query.find({
useMasterKey: true,
sessionToken: user.get('sessionToken'),
});
})
.then(results => {
const foundKeys = [];
expect(results.length).toBe(3);
for (const key in results) {
const sessionToken = results[key].get('sessionToken');
if (foundKeys[sessionToken] && sessionToken !== undefined) {
fail('Duplicate session token present in response');
break;
}
foundKeys[sessionToken] = 1;
}
done();
})
.catch(err => {
fail(err);
});
});
// multiple users with just sessionToken
it('token on users should retain original sessionTokens with just sessionToken set', done => {
let knownSessionToken;
setupTestUsers()
.then(user => {
knownSessionToken = user.get('sessionToken');
const query = new Parse.Query(Parse.User);
return query.find({
sessionToken: knownSessionToken,
});
})
.then(results => {
const foundKeys = [];
expect(results.length).toBe(3);
for (const key in results) {
const sessionToken = results[key].get('sessionToken');
if (foundKeys[sessionToken] && sessionToken !== undefined) {
fail('Duplicate session token present in response');
break;
}
foundKeys[sessionToken] = 1;
}
done();
})
.catch(err => {
fail(err);
});
});
});
+3295 -2567
View File
File diff suppressed because it is too large Load Diff
+16 -16
View File
@@ -1,42 +1,42 @@
var ParseWebSocket = require('../src/LiveQuery/ParseWebSocketServer').ParseWebSocket;
const ParseWebSocket = require('../lib/LiveQuery/ParseWebSocketServer')
.ParseWebSocket;
describe('ParseWebSocket', function() {
it('can be initialized', function() {
var ws = {};
var parseWebSocket = new ParseWebSocket(ws);
const ws = {};
const parseWebSocket = new ParseWebSocket(ws);
expect(parseWebSocket.ws).toBe(ws);
});
it('can handle events defined in typeMap', function() {
var ws = {
on: jasmine.createSpy('on')
const ws = {
on: jasmine.createSpy('on'),
};
var callback = {};
var parseWebSocket = new ParseWebSocket(ws);
const callback = {};
const parseWebSocket = new ParseWebSocket(ws);
parseWebSocket.on('disconnect', callback);
expect(parseWebSocket.ws.on).toHaveBeenCalledWith('close', callback);
});
it('can handle events which are not defined in typeMap', function() {
var ws = {
on: jasmine.createSpy('on')
const ws = {
on: jasmine.createSpy('on'),
};
var callback = {};
var parseWebSocket = new ParseWebSocket(ws);
const callback = {};
const parseWebSocket = new ParseWebSocket(ws);
parseWebSocket.on('open', callback);
expect(parseWebSocket.ws.on).toHaveBeenCalledWith('open', callback);
});
it('can send a message', function() {
var ws = {
send: jasmine.createSpy('send')
const ws = {
send: jasmine.createSpy('send'),
};
var parseWebSocket = new ParseWebSocket(ws);
parseWebSocket.send('message')
const parseWebSocket = new ParseWebSocket(ws);
parseWebSocket.send('message');
expect(parseWebSocket.ws.send).toHaveBeenCalledWith('message');
});
+16 -12
View File
@@ -1,11 +1,11 @@
var ParseWebSocketServer = require('../src/LiveQuery/ParseWebSocketServer').ParseWebSocketServer;
const ParseWebSocketServer = require('../lib/LiveQuery/ParseWebSocketServer')
.ParseWebSocketServer;
describe('ParseWebSocketServer', function() {
beforeEach(function(done) {
// Mock ws server
var EventEmitter = require('events');
var mockServer = function() {
const EventEmitter = require('events');
const mockServer = function() {
return new EventEmitter();
};
jasmine.mockLibrary('ws', 'Server', mockServer);
@@ -13,14 +13,18 @@ describe('ParseWebSocketServer', function() {
});
it('can handle connect event when ws is open', function(done) {
var onConnectCallback = jasmine.createSpy('onConnectCallback');
var http = require('http');
var server = http.createServer();
var parseWebSocketServer = new ParseWebSocketServer(server, onConnectCallback, 5).server;
var ws = {
const onConnectCallback = jasmine.createSpy('onConnectCallback');
const http = require('http');
const server = http.createServer();
const parseWebSocketServer = new ParseWebSocketServer(
server,
onConnectCallback,
5
).server;
const ws = {
readyState: 0,
OPEN: 0,
ping: jasmine.createSpy('ping')
ping: jasmine.createSpy('ping'),
};
parseWebSocketServer.emit('connection', ws);
@@ -31,10 +35,10 @@ describe('ParseWebSocketServer', function() {
expect(ws.ping).toHaveBeenCalled();
server.close();
done();
}, 10)
}, 10);
});
afterEach(function(){
afterEach(function() {
jasmine.restoreLibrary('ws', 'Server');
});
});
+1282 -941
View File
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