Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: parse-community/Parse-SDK-JS
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: ee33c48e4a19f9264fd4301cb1dd3250dc7550a7
Choose a base ref
...
head repository: parse-community/Parse-SDK-JS
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: 2e7a4068d77f3dda5924a645f4a57de13b155ec2
Choose a head ref

Commits on Jan 10, 2018

  1. Copy the full SHA
    7069564 View commit details
  2. test fix

    dplewis committed Jan 10, 2018
    Copy the full SHA
    7aa239d View commit details

Commits on Jan 15, 2018

  1. cleanup tests

    dplewis committed Jan 15, 2018
    Copy the full SHA
    3cd3f7c View commit details
  2. Merge pull request #541 from dplewis/full-text-search

    Support for Full Text Search Query
    montymxb authored Jan 15, 2018
    Copy the full SHA
    7ef1b0a View commit details

Commits on Jan 28, 2018

  1. Adds Purge & Polygon to Parse.Schema (#544)

    * Purge & Polygon to Parse.Schema
    
    * Purge & Polygon to Parse.Schema
    
    * Revert "Purge & Polygon to Parse.Schema"
    
    This reverts commit 8d95e05.
    
    * fix error
    dplewis authored and flovilmart committed Jan 28, 2018
    Copy the full SHA
    741958a View commit details

Commits on Feb 1, 2018

  1. Add Parse.Query.and() to request that all queries must match (#367)

    * Add Parse.Query.and() to request that all queries must match
    * Add test for Parse.Query.and()
    * Add integration tests for Parse.Query.and()
    gofabian authored and montymxb committed Feb 1, 2018
    Copy the full SHA
    3d7c97b View commit details

Commits on Mar 6, 2018

  1. Update README.md (#538)

    Added note for React Native applications regarding the new function for setting the AsyncStorage
    jcguarinpenaranda authored and flovilmart committed Mar 6, 2018
    Copy the full SHA
    23c22cd View commit details
  2. Copy the full SHA
    2364019 View commit details

Commits on Mar 8, 2018

  1. Copy the full SHA
    a86130a View commit details

Commits on Mar 10, 2018

  1. Add Jobs Functions to Cloud

    dplewis committed Mar 10, 2018
    Copy the full SHA
    024ce34 View commit details
  2. fix tests

    dplewis committed Mar 10, 2018
    Copy the full SHA
    79c916d View commit details
  3. added test cases

    fix flaky test
    dplewis committed Mar 10, 2018
    Copy the full SHA
    c4eee61 View commit details
  4. Merge pull request #555 from dplewis/jobs-cloud

    Add Jobs Functions to Cloud
    dplewis authored Mar 10, 2018
    Copy the full SHA
    cd057a5 View commit details
  5. Copy the full SHA
    0d97d5e View commit details
  6. Merge pull request #554 from dplewis/api-readme

    Add API Reference link to README
    dplewis authored Mar 10, 2018
    Copy the full SHA
    f71142a View commit details

Commits on Mar 15, 2018

  1. Update ParseFile to handle a data URI containing a number (#483)

    * Update ParseFile to handle datauri containing a number
    
    * Improve the regex to accept all valid mime types, and to handle the charset param
    AppOrchestra authored and montymxb committed Mar 15, 2018
    Copy the full SHA
    d7242d3 View commit details

Commits on May 22, 2018

  1. Copy the full SHA
    d91dc6d View commit details
  2. Make javascript key optional (#570)

    Close #569
    alvinthen authored and flovilmart committed May 22, 2018
    Copy the full SHA
    a395c5b View commit details

Commits on May 23, 2018

  1. New query condition to match all strings that starts with some other …

    …given strings (#437)
    
    * feat: Search $all strings starting with given strings
    
    * feat: Include dist & lib folders
    
    * Revert "feat: Include dist & lib folders"
    
    This reverts commit 4494b86.
    eduardbosch authored and dplewis committed May 23, 2018
    Copy the full SHA
    0fbc604 View commit details

Commits on Jun 25, 2018

  1. Introduce ability to not sort geo queries using nearSphere (#582)

    * added convenience methods and tests
    
    * added more tests
    
    * combined methos for sorted and unsorted queries by using `sorted` parameter
    
    * sorted parameter defaults to true if not set
    
    * made integration tests pending because tests again unpublished version of parse server will fail
    
    * removed duplicate of withinGeoBox that was mistakenly added earlier
    
    * integration tests running against latest parse-server version; re-activated integration tests for near-sphere
    
    * reverted existing test cases to without sorted parameter; added test case with sorted=true parameter
    
    * test against latest
    mtrezza authored and flovilmart committed Jun 25, 2018
    Copy the full SHA
    4a6e619 View commit details
  2. Copy the full SHA
    dd20b4b View commit details
  3. Bumps all dependencies with npm update (#586)

    * Bumps dependencies to the most recent versions
    
    * Bumps dev dependencies
    
    * pins dependencies
    
    * Use codecov package instead of codecov.io
    flovilmart authored Jun 25, 2018
    Copy the full SHA
    e8034f7 View commit details

Commits on Jun 26, 2018

  1. Greenkeeper/initial (#589)

    * chore: add Greenkeeper config file
    
    * docs(readme): add Greenkeeper badge
    flovilmart authored Jun 26, 2018
    Copy the full SHA
    c8e2a4a View commit details

Commits on Jun 29, 2018

  1. Insist on the difference between 'first' and 'get' (#590)

    Users shouldn't expect an undefined when calling get.
    Olivier Allouch authored and flovilmart committed Jun 29, 2018
    Copy the full SHA
    acdb645 View commit details

Commits on Jul 4, 2018

  1. Add options to full text search (#573)

    * Adding options to fullText search
    
    * adding tests
    
    * fix tests
    
    * add options validation
    
    * add options validation and more test cases
    
    * removing commented out code
    
    * add more test
    
    * sort by text score method
    
    * Text fix
    
    Indexes don't get deleted between tests so new classes are created as a workaround. Also I can't seem to use language options with case sensitive or diacritic sensitive
    danielbm authored and dplewis committed Jul 4, 2018
    Copy the full SHA
    de4bc1c View commit details

Commits on Jul 9, 2018

  1. Flovilmart/readme updates (#588)

    * chore(package): update dependencies
    
    https://greenkeeper.io/
    
    * chore(travis): whitelist greenkeeper branches 
    
    https://greenkeeper.io/
    
    * docs(readme): add Greenkeeper badge 
    
    https://greenkeeper.io/
    
    * fixes
    
    * Better gitignore
    
    * Update README.md
    
    * Update README.md
    
    * Update README.md
    flovilmart authored Jul 9, 2018
    Copy the full SHA
    2640391 View commit details

Commits on Jul 26, 2018

  1. Copy the full SHA
    112f00a View commit details
  2. Copy the full SHA
    e60d4c5 View commit details
  3. Copy the full SHA
    ceb7e4d View commit details
  4. Copy the full SHA
    25d13ed View commit details
  5. Moves integration test at top level with jasmine (#593)

    * Moves integration test at top level with jasmine
    
    - Use jasmine so toolchain is similar to parse-server
    - Replaces bash script with pre-test
    
    * Update contributing guide
    
    * Updates URLs
    
    * nits
    
    * nits
    
    * nit
    flovilmart authored and dplewis committed Jul 26, 2018
    Copy the full SHA
    358a335 View commit details

Commits on Jul 29, 2018

  1. Copy the full SHA
    5b3379f View commit details
  2. Update CONTRIBUTING.md (#615)

    flovilmart authored and dplewis committed Jul 29, 2018

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
    Copy the full SHA
    a0f050d View commit details
  3. Pinned gulp-watch (#616)

    flovilmart authored and dplewis committed Jul 29, 2018
    Copy the full SHA
    b762756 View commit details

Commits on Aug 4, 2018

  1. SDK v2 (#620)

    * Modernizes SDK
    
    - Replace ParsePromise with native Promise
    - Removes all compatibility with Backbone style callbacks
    
    * Ensures integration tests are OK
    
    * Updates documentations to remove backbone styles
    
    * removes es2015 transformations
    
    * Adds prepare phase
    
    * Adds changelog
    flovilmart authored Aug 4, 2018
    Copy the full SHA
    c344fd4 View commit details
  2. Copy the full SHA
    b9b4564 View commit details

Commits on Aug 6, 2018

  1. Fix documentation and type annotations for include and select (#601)

    These both can accept either a string, or an array of strings
    Tyler Breisacher authored and flovilmart committed Aug 6, 2018
    Copy the full SHA
    bb8955f View commit details
  2. Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
    Copy the full SHA
    688e936 View commit details

Commits on Aug 9, 2018

  1. Copy the full SHA
    64aacec View commit details
  2. ⚡ Release v2.0.1 (#624)

    * ⚡ Release v2.0.1
    
    * Adds documentation for async functions
    
    * Adds documentation about 2.0.0
    
    * Adds upgrade guide
    
    * Reverts CloudCode docs
    flovilmart authored Aug 9, 2018

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
    Copy the full SHA
    2e69f39 View commit details

Commits on Aug 10, 2018

  1. Copy the full SHA
    6d5112a View commit details
  2. Support includeAll Query (#632)

    * includeAll
    
    * added * to include
    
    * removed unnessary fields
    
    * include server version
    dplewis authored Aug 10, 2018

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
    Copy the full SHA
    000c1fb View commit details

Commits on Aug 11, 2018

  1. Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
    Copy the full SHA
    506bc83 View commit details

Commits on Aug 12, 2018

  1. Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
    Copy the full SHA
    4fd73b0 View commit details
  2. Support nor Query (#634)

    * nor
    
    * doc fix
    dplewis authored Aug 12, 2018

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
    Copy the full SHA
    ceb34c5 View commit details
  3. Support Fetch With Include (#631)

    * fetchWithInclude
    
    * improve coverage
    
    * Add fetchWithInclude to Users
    
    * improve docs
    
    * nit
    dplewis authored Aug 12, 2018

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
    Copy the full SHA
    1353997 View commit details

Commits on Aug 13, 2018

  1. Allow aggregate stages with same name (#637)

    * Allow aggregate stages with same name
    
    Instead of converting array to json for the payload just pass it though.
    
    The pipeline keyword needs to be added to the server.
    
    * nit
    
    * test update
    dplewis authored Aug 13, 2018

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
    Copy the full SHA
    ca9000e View commit details
  2. Adds eslint on src and src/__tests__ (#638)

    * Adds linting to tests
    
    * Adds linting to sources
    
    * Adds linting
    
    * Adds linting for integration spec
    
    * Updates docs with options removed where necessary
    flovilmart authored Aug 13, 2018

    Partially verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
    We cannot verify signatures from co-authors, and some of the co-authors attributed to this commit require their commits to be signed.
    Copy the full SHA
    db59b63 View commit details

Commits on Aug 15, 2018

  1. Fixes issue #639 (#640)

    * Adds failing test for #639
    
    * Fixes #639
    flovilmart authored Aug 15, 2018

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
    Copy the full SHA
    910f2f4 View commit details

Commits on Aug 16, 2018

  1. Removal of the 'wait=true" comment in destroy (#643)

    I couldn't find any trace of this 'wait' option in the code (sdk or server)
    Olivier Allouch authored and flovilmart committed Aug 16, 2018
    Copy the full SHA
    81e8c44 View commit details
Showing with 52,567 additions and 13,409 deletions.
  1. +69 −0 .eslintrc.json
  2. +15 −0 .gitattributes
  3. +47 −0 .github/ISSUE_TEMPLATE/---1-report-an-issue.md
  4. +20 −0 .github/ISSUE_TEMPLATE/---2-feature-request.md
  5. +8 −0 .github/ISSUE_TEMPLATE/config.yml
  6. BIN .github/parse-logo.png
  7. +32 −0 .github/stale.yml
  8. +46 −0 .github/workflows/ci.yml
  9. +44 −0 .github/workflows/pages.yml
  10. +24 −0 .github/workflows/release.yml
  11. +2 −0 .gitignore
  12. +5 −0 .prettierrc
  13. +0 −51 .travis.yml
  14. +6 −0 .vscode/settings.json
  15. +105 −0 2.0.0.md
  16. +423 −0 CHANGELOG.md
  17. +1 −1 CODE_OF_CONDUCT.md
  18. +78 −24 CONTRIBUTING.md
  19. +99 −22 README.md
  20. +8 −2 babel-jest.js
  21. +66 −0 build_releases.js
  22. +0 −17 build_releases.sh
  23. +87 −31 gulpfile.js
  24. +0 −11 integration/package.json
  25. +0 −27 integration/server.js
  26. +20 −0 integration/test/.eslintrc.json
  27. +381 −315 integration/test/ArrayOperationsTest.js
  28. +13 −0 integration/test/CustomAuth.js
  29. +197 −173 integration/test/DirtyTest.js
  30. +71 −0 integration/test/IdempotencyTest.js
  31. +207 −171 integration/test/IncrementTest.js
  32. +421 −501 integration/test/ParseACLTest.js
  33. +154 −0 integration/test/ParseCloudTest.js
  34. +44 −0 integration/test/ParseConfigTest.js
  35. +256 −0 integration/test/ParseEventuallyQueueTest.js
  36. +115 −0 integration/test/ParseFileTest.js
  37. +75 −82 integration/test/ParseGeoBoxTest.js
  38. +353 −164 integration/test/ParseGeoPointTest.js
  39. +259 −0 integration/test/ParseLiveQueryTest.js
  40. +2,890 −0 integration/test/ParseLocalDatastoreTest.js
  41. +15 −32 integration/test/ParseMasterKeyTest.js
  42. +1,700 −956 integration/test/ParseObjectTest.js
  43. +138 −86 integration/test/ParsePolygonTest.js
  44. +15 −0 integration/test/ParsePushTest.js
  45. +85 −36 integration/test/ParseQueryAggregateTest.js
  46. +1,961 −1,015 integration/test/ParseQueryTest.js
  47. +230 −182 integration/test/ParseRelationTest.js
  48. +0 −33 integration/test/ParseRoleTest.js
  49. +440 −124 integration/test/ParseSchemaTest.js
  50. +34 −0 integration/test/ParseServerTest.js
  51. +157 −128 integration/test/ParseSubclassTest.js
  52. +960 −281 integration/test/ParseUserTest.js
  53. +10 −3 integration/test/clear.js
  54. +50 −0 integration/test/cloud/main.js
  55. +146 −0 integration/test/helper.js
  56. +38 −0 integration/test/mockLocalStorage.js
  57. +45 −0 integration/test/mockRNStorage.js
  58. +5 −0 integration/test/sleep.js
  59. +11 −0 jasmine.json
  60. +2 −2 jsdoc-conf.json
  61. +14,656 −0 package-lock.json
  62. +94 −36 package.json
  63. +5 −5 release_docs.sh
  64. +0 −11 run_integration.sh
  65. +3 −4 src/.flowconfig
  66. +39 −53 src/Analytics.js
  67. +130 −0 src/AnonymousUtils.js
  68. +99 −56 src/Cloud.js
  69. +175 −62 src/CloudCode.js
  70. +232 −146 src/CoreManager.js
  71. +25 −0 src/CryptoController.js
  72. +4 −1 src/EventEmitter.js
  73. +375 −0 src/EventuallyQueue.js
  74. +91 −100 src/FacebookUtils.js
  75. +32 −0 src/IndexedDBStorageController.js
  76. +9 −26 src/InstallationController.js
  77. +184 −135 src/LiveQueryClient.js
  78. +52 −33 src/LiveQuerySubscription.js
  79. +408 −0 src/LocalDatastore.js
  80. +74 −0 src/LocalDatastoreController.js
  81. +95 −0 src/LocalDatastoreController.react-native.js
  82. +24 −0 src/LocalDatastoreUtils.js
  83. +70 −29 src/ObjectStateMutations.js
  84. +565 −0 src/OfflineQuery.js
  85. +222 −79 src/Parse.js
  86. +48 −59 src/ParseACL.js
  87. +612 −0 src/ParseCLP.js
  88. +98 −41 src/ParseConfig.js
  89. +174 −122 src/ParseError.js
  90. +384 −70 src/ParseFile.js
  91. +52 −64 src/ParseGeoPoint.js
  92. +49 −53 src/ParseHooks.js
  93. +2 −2 src/ParseInstallation.js
  94. +63 −148 src/ParseLiveQuery.js
  95. +1,280 −592 src/ParseObject.js
  96. +104 −102 src/ParseOp.js
  97. +50 −39 src/ParsePolygon.js
  98. +0 −589 src/ParsePromise.js
  99. +1,234 −418 src/ParseQuery.js
  100. +28 −26 src/ParseRelation.js
  101. +16 −22 src/ParseRole.js
  102. +224 −184 src/ParseSchema.js
  103. +26 −36 src/ParseSession.js
  104. +538 −370 src/ParseUser.js
  105. +67 −55 src/Push.js
  106. +208 −119 src/RESTController.js
  107. +29 −24 src/SingleInstanceStateController.js
  108. +36 −0 src/Socket.weapp.js
  109. +37 −26 src/Storage.js
  110. +13 −4 src/StorageController.browser.js
  111. +9 −4 src/StorageController.default.js
  112. +67 −31 src/StorageController.react-native.js
  113. +42 −0 src/StorageController.weapp.js
  114. +27 −22 src/TaskQueue.js
  115. +30 −25 src/UniqueInstanceStateController.js
  116. +86 −0 src/Xhr.weapp.js
  117. +38 −0 src/__tests__/.eslintrc.json
  118. +33 −35 src/__tests__/Analytics-test.js
  119. +88 −0 src/__tests__/AnonymousUtils-test.js
  120. +296 −51 src/__tests__/Cloud-test.js
  121. +245 −147 src/__tests__/CoreManager-test.js
  122. +433 −0 src/__tests__/EventuallyQueue-test.js
  123. +308 −0 src/__tests__/FacebookUtils-test.js
  124. +229 −150 src/__tests__/Hooks-test.js
  125. +39 −23 src/__tests__/InstallationController-test.js
  126. +713 −120 src/__tests__/LiveQueryClient-test.js
  127. +940 −0 src/__tests__/LocalDatastore-test.js
  128. +110 −30 src/__tests__/ObjectStateMutations-test.js
  129. +787 −0 src/__tests__/OfflineQuery-test.js
  130. +185 −2 src/__tests__/Parse-test.js
  131. +87 −80 src/__tests__/ParseACL-test.js
  132. +680 −0 src/__tests__/ParseCLP-test.js
  133. +194 −40 src/__tests__/ParseConfig-test.js
  134. +11 −3 src/__tests__/ParseError-test.js
  135. +772 −49 src/__tests__/ParseFile-test.js
  136. +53 −25 src/__tests__/ParseGeoPoint-test.js
  137. +28 −0 src/__tests__/ParseInstallation-test.js
  138. +158 −62 src/__tests__/ParseLiveQuery-test.js
  139. +2,481 −774 src/__tests__/ParseObject-test.js
  140. +221 −106 src/__tests__/ParseOp-test.js
  141. +97 −0 src/__tests__/ParsePolygon-test.js
  142. +0 −713 src/__tests__/ParsePromise-test.js
  143. +2,745 −812 src/__tests__/ParseQuery-test.js
  144. +142 −57 src/__tests__/ParseRelation-test.js
  145. +53 −28 src/__tests__/ParseRole-test.js
  146. +249 −174 src/__tests__/ParseSchema-test.js
  147. +51 −28 src/__tests__/ParseSession-test.js
  148. +1,493 −296 src/__tests__/ParseUser-test.js
  149. +44 −41 src/__tests__/Push-test.js
  150. +511 −145 src/__tests__/RESTController-test.js
  151. +472 −161 src/__tests__/SingleInstanceStateController-test.js
  152. +233 −107 src/__tests__/Storage-test.js
  153. +83 −31 src/__tests__/TaskQueue-test.js
  154. +115 −110 src/__tests__/UniqueInstanceStateController-test.js
  155. +15 −15 src/__tests__/arrayContainsObject-test.js
  156. +152 −0 src/__tests__/browser-test.js
  157. +49 −30 src/__tests__/canBeSerialized-test.js
  158. +49 −34 src/__tests__/decode-test.js
  159. +66 −48 src/__tests__/encode-test.js
  160. +49 −16 src/__tests__/equals-test.js
  161. +3 −4 src/__tests__/escape-test.js
  162. +2 −2 src/__tests__/parseDate-test.js
  163. +19 −0 src/__tests__/promiseUtils-test.js
  164. +81 −0 src/__tests__/react-native-test.js
  165. +7 −7 src/__tests__/test_helpers/asyncHelper.js
  166. +25 −0 src/__tests__/test_helpers/mockAsyncStorage.js
  167. +34 −0 src/__tests__/test_helpers/mockIndexedDB.js
  168. +45 −0 src/__tests__/test_helpers/mockRNStorage.js
  169. +39 −0 src/__tests__/test_helpers/mockStorageInteface.js
  170. +79 −0 src/__tests__/test_helpers/mockWeChat.js
  171. +17 −7 src/__tests__/test_helpers/mockXHR.js
  172. +24 −29 src/__tests__/unique-test.js
  173. +62 −57 src/__tests__/unsavedChildren-test.js
  174. +83 −0 src/__tests__/weapp-test.js
  175. +4 −6 src/arrayContainsObject.js
  176. +5 −5 src/canBeSerialized.js
  177. +7 −8 src/decode.js
  178. +39 −21 src/encode.js
  179. +21 −8 src/equals.js
  180. +4 −4 src/escape.js
  181. +48 −0 src/interfaces/AuthProvider.js
  182. +2 −1 src/interfaces/react-native.js
  183. +1 −1 src/interfaces/xmlhttprequest.js
  184. +15 −12 src/parseDate.js
  185. +80 −0 src/promiseUtils.js
  186. +2 −2 src/unique.js
  187. +20 −20 src/unsavedChildren.js
  188. +22 −0 src/uuid.js
  189. +0 −1 vendor/README.md
  190. +0 −171 vendor/babel-plugin-dead-code-elimination/index.js
  191. +0 −7 vendor/babel-plugin-dead-code-elimination/package.json
  192. +1 −0 weapp.js
69 changes: 69 additions & 0 deletions .eslintrc.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
{
"root": true,
"extends": [
"eslint:recommended",
"plugin:jsdoc/recommended"
],
"env": {
"node": true,
"es6": true
},
"parser": "babel-eslint",
"globals": {
"wx": true
},
"plugins": [
"flowtype",
"jsdoc"
],
"parserOptions": {
"ecmaVersion": 6,
"sourceType": "module"
},
"rules": {
"indent": ["error", 2],
"linebreak-style": ["error", "unix"],
"no-trailing-spaces": 2,
"eol-last": 2,
"space-in-parens": ["error", "never"],
"no-multiple-empty-lines": 1,
"prefer-const": "error",
"space-infix-ops": "error",
"no-useless-escape": "off",
"no-var": "error",
"no-console": 0,
"no-prototype-builtins": "off",
"require-atomic-updates": "off",
"jsdoc/require-jsdoc": 0,
"jsdoc/require-returns-description": 0,
"jsdoc/require-param-description": 0,
"jsdoc/require-property-description": 0,
"jsdoc/require-param-type": 0,
"jsdoc/check-param-names": [
"error",
{
"allowExtraTrailingParamDocs": true
}
],
"jsdoc/check-tag-names": [
"error",
{
"definedTags": [
"flow",
"flow-weak"
]
}
],
"jsdoc/no-undefined-types": [
"error",
{
"definedTypes": [
"AuthProvider",
"AsyncStorage",
"LocalDatastoreController",
"Parse"
]
}
]
}
}
15 changes: 15 additions & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
@@ -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
47 changes: 47 additions & 0 deletions .github/ISSUE_TEMPLATE/---1-report-an-issue.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
---
name: "\U0001F41B Report an issue"
about: A feature is not working as expected.
title: ''
labels: ''
assignees: ''

---

### New Issue Checklist
<!-- Please check the following boxes [ ] -> [x] before submitting your issue. Click the "Preview" tab for better readability. Thanks for reporting this issue! -->

- [ ] I am not disclosing a [vulnerability](https://github.com/parse-community/parse-server/blob/master/SECURITY.md).
- [ ] I am not just asking a [question](https://github.com/parse-community/.github/blob/master/SUPPORT.md).
- [ ] I have searched through [existing issues](https://github.com/parse-community/Parse-SDK-JS/issues?q=is%3Aissue).
- [ ] I can reproduce the issue with the latest versions of [Parse Server](https://github.com/parse-community/parse-server/releases) and the [Parse JS SDK](https://github.com/parse-community/Parse-SDK-JS/releases). <!-- We don't investigate issues for outdated releases. -->

### Issue Description
<!-- What is the specific issue? -->

### Steps to reproduce
<!-- How can someone else reproduce the issue? -->

### Actual Outcome
<!-- What outcome, for example query result, did you get? -->

### Expected Outcome
<!-- What outcome, for example query result, did you expect? -->

### Environment
<!-- Be specific with versions, don't use "latest" or semver ranges like "~x.y.z" or "^x.y.z". -->

Server
- Parse Server version: `FILL_THIS_OUT`
- Operating system: `FILL_THIS_OUT`
- Local or remote host (AWS, Azure, Google Cloud, Heroku, Digital Ocean, etc): `FILL_THIS_OUT`

Database
- System (MongoDB or Postgres): `FILL_THIS_OUT`
- Database version: `FILL_THIS_OUT`
- Local or remote host (MongoDB Atlas, mLab, AWS, Azure, Google Cloud, etc): `FILL_THIS_OUT`

Client
- Parse JS SDK version: `FILL_THIS_OUT`

### Logs
<!-- Include relevant logs here. Turn on additional logging by configuring VERBOSE=1 in your environment. -->
20 changes: 20 additions & 0 deletions .github/ISSUE_TEMPLATE/---2-feature-request.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
---
name: "\U0001F4A1 Request a feature"
about: Suggest new functionality or an enhancement of existing functionality.
title: ''
labels: ''
assignees: ''

---

**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.
8 changes: 8 additions & 0 deletions .github/ISSUE_TEMPLATE/config.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
blank_issues_enabled: false
contact_links:
- name: 🙋🏽‍♀️ Getting help with code
url: https://stackoverflow.com/questions/tagged/parse-platform+parse-javascript-sdk
about: Get help with code-level questions on Stack Overflow.
- name: 🙋 Getting general help
url: https://community.parseplatform.org/c/client-sdks/javascript-sdk
about: Get help with other questions on our Community Forum.
Binary file added .github/parse-logo.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
32 changes: 32 additions & 0 deletions .github/stale.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
# 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:
- accepted
- pinned
- security
- good first task
- up-for-grabs
- enhancement
- help wanted
- bug
- greenkeeper
- feature
- needs more info
- discussion
- question
- pr-submitted
- proposal
# Label to use when marking an issue as stale
staleLabel: stale
# 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
46 changes: 46 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
name: ci
on:
push:
branches:
- master
pull_request:
branches:
- '**'
jobs:
check-lock-file-version:
name: NPM Lock File Version
timeout-minutes: 5
runs-on: ubuntu-18.04
steps:
- uses: actions/checkout@v2
- name: Check NPM lock file version
uses: mansona/npm-lockfile-version@v1
with:
version: 1
build:
runs-on: ubuntu-18.04
timeout-minutes: 30
env:
MONGODB_VERSION: 3.6.9
steps:
- uses: actions/checkout@v2
- name: Use Node.js
uses: actions/setup-node@v1
with:
node-version: '10.14'
- name: Cache Node.js modules
uses: actions/cache@v2
with:
path: ~/.npm
key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}
restore-keys: |
${{ runner.os }}-node-
- run: npm ci
- run: npm install -g mongodb-runner
- run: mongodb-runner start
- run: npm run lint
- run: npm test -- --maxWorkers=4
- run: npm run integration
env:
CI: true
- run: bash <(curl -s https://codecov.io/bash)
44 changes: 44 additions & 0 deletions .github/workflows/pages.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
name: docs
on:
release:
types: [published]
jobs:
build:
runs-on: ubuntu-18.04
timeout-minutes: 30
steps:
- uses: actions/checkout@v2
- name: Use Node.js
uses: actions/setup-node@v1
with:
node-version: '10.14'
- name: Cache Node.js modules
uses: actions/cache@v2
with:
path: ~/.npm
key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}
restore-keys: |
${{ runner.os }}-node-
- name: Get Tag
uses: actions/github-script@v3
id: tag
with:
github-token: ${{secrets.GITHUB_TOKEN}}
result-encoding: string
script: |
const ref = process.env.GITHUB_REF
if(!ref.startsWith('refs/tags/'))
return ''
return ref.replace(/^refs\/tags\//, '')
- name: Generate Docs
run: |
echo $SOURCE_TAG
npm ci
npm run release_docs
env:
SOURCE_TAG: ${{ steps.tag.outputs.result }}
- name: Deploy
uses: peaceiris/actions-gh-pages@v3.7.3
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
publish_dir: ./docs
24 changes: 24 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
name: release
on:
release:
types: [published]
jobs:
publish-npm:
runs-on: ubuntu-18.04
steps:
- uses: actions/checkout@v2
- uses: actions/setup-node@v1
with:
node-version: 12
registry-url: https://registry.npmjs.org/
- name: Cache Node.js modules
uses: actions/cache@v2
with:
path: ~/.npm
key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}
restore-keys: |
${{ runner.os }}-node-
- run: npm ci
- run: npm publish
env:
NODE_AUTH_TOKEN: ${{secrets.NPM_TOKEN}}
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -9,6 +9,8 @@ integration/test_logs
.DS_Store
.idea/
integration/test_logs
test_logs
out/
docs/
.eslintcache

5 changes: 5 additions & 0 deletions .prettierrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
semi: true
trailingComma: "es5"
singleQuote: true
arrowParens: "avoid"
printWidth: 100
51 changes: 0 additions & 51 deletions .travis.yml

This file was deleted.

6 changes: 6 additions & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"jasmineExplorer.config": "jasmine.json",
"jasmineExplorer.env": {
"TESTING": "1"
}
}
105 changes: 105 additions & 0 deletions 2.0.0.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
# Upgrading Parse SDK to version 2.0.0

With Parse SDK 2.0.0, gone are the backbone style callbacks and Parse.Promises.

Instead, the Parse SDK 2.0.0 uses native promises in the browser, node and react native that allows for a more standard API.

Migrating to native Promises should be straightforward, we'll recap the different changes:

## `.done()` and `.fail()` continuations

With native promises, `.done` and `.fail` don't exist and are replaces by `.then` and `.catch`

```js
// before
const query = new Parse.Query();
query.find()
.done((results) => {

})
.fail((error) => {

});

// after
query.find()
.then((results) => {

})
.catch((error) => {

});
```

## `.always()` is replaced by `.finally()`

```js
// before
const query = new Parse.Query();
query.find()
.always((result) => {

});

// after
query.find()
.finally((result) => {

});
```

## `new Parse.Promise()`

With native promises, the constructor is different, you will need to update to the native constructor which requires moving the code around.

```js
// before
const promise = new Parse.Promise();
doSomethingAsync((error, success) => {
if (error) { promise.reject(error); }
else { promise.resolve(success); }
});
return promise;

// after
return new Promise((resolve, reject) => {
doSomethingAsync((error, success) => {
if (error) { reject(error); }
else { resolve(success); }
});
});
```

## Static methods

- `Parse.Promise.as` is replaced by `Promise.resolve`
- `Parse.Promise.error` is replaced by `Promise.reject`
- `Parse.Promise.when` is replaced by `Promise.all`

:warning: `Promise.all` only takes an array or an iterable promises.

```js
// before
Parse.Promise.when(promise1, promise2, promise3)
.then((result1, result2, result3) => {

});

// after
Promise.all([promise1, promise2, promise3])
.then(([result1, result2, result3]) => {

});
```

:warning: Whereas `Parse.Promise.always`, `Promise.finally` callback don't receive any arguments, and don't resolve with a new argument to pass to the next promise in chain.

```js
// before
Parse.Promise.as(1).always((val) => val + 1).then((result) => console.log(result))
// will print 2

// after
Promise.resolve(1).finally(() => 2).then((result) => console.log(result))
// will print 1
```
423 changes: 423 additions & 0 deletions CHANGELOG.md

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion CODE_OF_CONDUCT.md
Original file line number Diff line number Diff line change
@@ -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.

102 changes: 78 additions & 24 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
@@ -1,66 +1,120 @@
# Contributing to the Parse JavaScript SDK
We want to make contributing to this project as easy and transparent as possible.

If you're looking to get started, but want to ease yourself into the codebase, look for issues tagged [good first bug](https://github.com/ParsePlatform/Parse-SDK-JS/labels/good%20first%20bug). These are simple yet valuable tasks that should be easy to get started.
We want to make contributing to this project as easy and transparent as possible.

## Code of Conduct
Facebook has adopted a Code of Conduct that we expect project participants to adhere to. Please read [the full text](https://code.facebook.com/codeofconduct) so that you can understand what actions will and will not be tolerated.
If you're looking to get started, but want to ease yourself into the codebase, look for issues tagged [good first task](https://github.com/parse-community/Parse-SDK-JS/labels/good%20first%20task). These are simple yet valuable tasks that should be easy to get started.

## Our Development Process
Most of our work will be done in public directly on GitHub. There may be changes done through our internal source control, but it will be rare and only as needed.
## `master` is unsafe

### `master` is unsafe
Our goal is to keep `master` stable, but there may be changes that your application may not be compatible with. We'll do our best to publicize any breaking changes, but try to use our specific releases in any production environment.

## Setting up the project for debugging and contributing:

### Recommended setup:

* [vscode](https://code.visualstudio.com), the popular IDE.
* [Jest Extension](https://marketplace.visualstudio.com/items?itemName=Orta.vscode-jest) the Jest extension for vscode to run the tests inline and debug quicky.
* [Jasmine Test Explorer Extension](https://marketplace.visualstudio.com/items?itemName=hbenl.vscode-test-explorer), a very practical test exploration plugin which let you run, debug and see the test results inline.
* [mongodb-runner](https://github.com/mongodb-js/runner) Easily install and run MongoDB to test your code against it. (install with `npm install -g mongodb-runner`)

### Setting up you local machine:

* [Fork](https://github.com/parse-community/Parse-SDK-JS) this project and clone the fork on your local machine:

```sh
$ git clone https://github.com/parse-community/Parse-SDK-JS
$ cd Parse-SDK-JS # go into the clone directory
$ npm install # install all the node dependencies
$ code . # launch vscode
```

### Building the SDK

The Parse JS SDK is built for three platforms:

- The browser
- nodejs
- react-native

When developing the SDK you can use `npm run watch` in order to rebuild your changes upon each save.

By default, the watch command will rebuild the SDK for the browser platform. The following commands will rebuild changes for a specific platform.

- `npm run watch:node`
- `npm run watch:browser`
- `npm run watch:react-native`

### Testing the code

The SDK is tested through two lenses. unit tests are run with jest and integration tests with jasmine.

Two different frameworks are used as the integration tests leverage a stateful server, with the data saved into the database, and Jest is running many tests in parallel, which makes it incompatible with our integration tests.

#### Unit tests

Those tests are located in [/src/__tests__](/src/__tests__) and are responsible for ensuring each class is behaving as expected, without considering the rest of the system. For example, adding a new query helper function would probably require to add relevant tests that ensure the query is properly serialized to a valid Parse REST query object. Unit tests heavily leverage mocking and are an essential part of our testing harness.

To run unit tests, run `npm test`. If you have the vscode Jest plugin extension (as recommended), you can run your tests by clicking the *Debug* lens that appears near by the test.

#### Integration tests

Those tests are located in [/integration/test](/integration/test) and are responsible for ensuring a proper communication with parse-server. With the integration tests, we ensure all communications between the SDK and the server are behaving accordingly.

To run the integration tests, you will need a valid mongodb running on your local machine. You can get easily mongodb running with `mongodb-runner` (see [Recommended setup](#recommended-setup)).

Use `npm run integration` in order to run the integration tests. If you have the vscode Jasmine extension installed (as recommended), you can run your tests by clicking the *Run* or the *Debug* lens that appears near by the test.

### Pull Requests

We actively welcome your pull requests. When we get one, we'll run some Parse-specific integration tests on it first. From here, we'll need to get a core member to sign off on the changes and then merge the pull request. For API changes we may need to fix internal uses, which could cause some delay. We'll do our best to provide updates and feedback throughout the process.

1. Fork the repo and create your branch from `master`.
2. Add unit tests for any new code you add.
3. If you've changed APIs, update the documentation.
4. Ensure the test suite passes.
4. Ensure the test suite passes. (run `npm test && npm run integration`)
5. Make sure your code lints.
6. If you haven't already, complete the Contributor License Agreement ("CLA").

### Contributor License Agreement ("CLA")
In order to accept your pull request, we need you to submit a CLA. You only need to do this once to work on any of Facebook's open source projects.

Complete your CLA here: <https://developers.facebook.com/opensource/cla>


## Bugs
Although we try to keep developing on Parse easy, you still may run into some issues. General questions should be asked on [Google Groups][google-group], technical questions should be asked on [Stack Overflow][stack-overflow], and for everything else we'll be using GitHub issues.

### Known Issues

We use GitHub issues to track public bugs. We will keep a close eye on this and try to make it clear when we have an internal fix in progress. Before filing a new issue, try to make sure your problem doesn't already exist.

### Reporting New Issues

Not all issues are SDK issues. If you're unsure whether your bug is with the SDK or backend, you can test to see if it reproduces with our [REST API][rest-api] and [Parse API Console][parse-api-console]. If it does, you can report backend bugs [here][bug-reports].
If the issue only reproduces with the JS SDK, you can [open an issue](https://github.com/parse-community/parse-server/issues) on this repository.

Details are key. The more information you provide us the easier it'll be for us to debug and the faster you'll receive a fix. Some examples of useful tidbits:

* A description. What did you expect to happen and what actually happened? Why do you think that was wrong?
* A simple unit test that fails. Refer [here][tests-dir] for examples of existing unit tests. See our [README](README.md#usage) for how to run unit tests. You can submit a pull request with your failing unit test so that our CI verifies that the test fails.
* A simple unit test that fails. Refer [here][tests-dir] for examples of existing unit tests and [here][integration-test-dir] for integration tests examples. See for how to setup your machine and run unit tests in [this](#setting-up-the-project-for-debugging-and-contributing) guide. You can submit a pull request with your failing unit test so that our CI verifies that the test fails.
* What version does this reproduce on? What version did it last work on?
* [Stacktrace or GTFO][stacktrace-or-gtfo]. In all honesty, full stacktraces with line numbers make a happy developer.
* Anything else you find relevant.

### Security Bugs
Facebook has a [bounty program](https://www.facebook.com/whitehat/) for the safe disclosure of security bugs. In those cases, please go through the process outlined on that page and do not file a public issue.

Parse Community has a [responsible Vulnerability Disclosure Program](https://github.com/parse-community/parse-server/blob/master/SECURITY.md) for the safe disclosure of security bugs. In those cases, please go through the process outlined on that page and do not file a public issue.

## Coding Style

* Most importantly, match the existing code style as much as possible.
* We use [Flow](http://flowtype.org/) and ES6 for this codebase. Use modern syntax whenever possible.
* Keep lines within 80 characters.
* Always end lines with semicolons.

### 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.

## License

By contributing to the Parse JavaScript SDK, you agree that your contributions will be licensed under its license.

[google-group]: https://groups.google.com/forum/#!forum/parse-developers
[stack-overflow]: http://stackoverflow.com/tags/parse.com
[bug-reports]: https://www.parse.com/help#report
[rest-api]: https://www.parse.com/docs/rest/guide
[parse-api-console]: http://blog.parse.com/announcements/introducing-the-parse-api-console/
[stack-overflow]: http://stackoverflow.com/tags/parse-server
[bug-reports]: https://github.com/parse-community/parse-server/issues
[rest-api]: https://docs.parseplatform.org/rest/guide
[parse-api-console]: http://blog.parseplatform.org/announcements/introducing-the-parse-api-console/
[stacktrace-or-gtfo]: http://i.imgur.com/jacoj.jpg
[tests-dir]: /src/__tests__
[integration-test-dir]: /integration/test
121 changes: 99 additions & 22 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,12 +1,31 @@
# Parse SDK for JavaScript

[![Build Status][build-status-svg]][build-status-link]
[![Test Coverage][coverage-status-svg]][coverage-status-link]
[![Npm Version][npm-svg]][npm-link]
[![CDNJS version](https://img.shields.io/cdnjs/v/parse.svg)](https://cdnjs.com/libraries/parse)
[![License][license-svg]][license-link]

A library that gives you access to the powerful Parse cloud platform from your JavaScript app. For more information on Parse and its features, see [the website](http://parseplatform.org) or [the JavaScript guide](http://docs.parseplatform.org/js/guide/).
<p align="center">
<img alt="Parse Platform" src="https://github.com/parse-community/Parse-SDK-JS/raw/master/.github/parse-logo.png" width="200">
</p>

<h2 align="center">Parse SDK for JavaScript</h2>

<p align="center">
A library that gives you access to the powerful Parse Server backend from your JavaScript app.
</p>

<p align="center">
<a href="https://twitter.com/intent/follow?screen_name=parseplatform"><img alt="Follow on Twitter" src="https://img.shields.io/twitter/follow/parseplatform?style=social&label=Follow"></a>
<a href="https://community.parseplatform.org/"><img alt="Join the conversation" src="https://img.shields.io/discourse/https/community.parseplatform.org/topics.svg"></a>
<a href="https://github.com/parse-community/Parse-SDK-JS/blob/master/LICENSE"><img alt="License" src="https://img.shields.io/badge/license-BSD-lightgrey.svg"></a>
<a href="https://github.com/parse-community/Parse-SDK-JS/actions?query=workflow%3Aci+branch%3Amaster"><img alt="Build status" src="https://github.com/parse-community/Parse-SDK-JS/workflows/ci/badge.svg?branch=master"></a>
<a href="#backers"><img alt="Backers on Open Collective" src="https://opencollective.com/parse-server/backers/badge.svg" /></a>
<a href="#sponsors"><img alt="Sponsors on Open Collective" src="https://opencollective.com/parse-server/sponsors/badge.svg" /></a>
</p>

<p align="center">
<a href="http://codecov.io/github/parse-community/Parse-SDK-JS?branch=master"><img alt="Test coverage" src="http://codecov.io/github/parse-community/Parse-SDK-JS/coverage.svg?branch=master"></a>
<a href="https://npmjs.org/parse"><img alt="npm version" src="https://badge.fury.io/js/parse.svg"></a>
<a href="https://cdnjs.com/libraries/parse"><img alt="CDNJS version" src="https://img.shields.io/cdnjs/v/parse.svg"></a>
<a href="https://greenkeeper.io/"><img alt="Greenkeeper badge" src="https://badges.greenkeeper.io/parse-community/Parse-SDK-JS.svg"></a>
</p>
<br>

For more information on Parse and its features, see [the website](https://parseplatform.org), [the JavaScript guide](https://docs.parseplatform.org/js/guide/), [the Cloud Code guide](https://docs.parseplatform.org/cloudcode/guide/) or [API Reference](https://parseplatform.org/Parse-SDK-JS/api/).

## Getting Started

@@ -20,22 +39,80 @@ The JavaScript ecosystem is wide and incorporates a large number of platforms an
To use the npm modules for a browser based application, include it as you normally would:

```js
var Parse = require('parse');
const Parse = require('parse');
// ES6 Minimized
import Parse from 'parse/dist/parse.min.js';
```

For web worker or browser applications, indexedDB storage is available:

```js
Parse.CoreManager.setStorageController(Parse.IndexedDB);
```

For server-side applications or Node.js command line tools, include `'parse/node'`:

```js
// In a node.js environment
var Parse = require('parse/node');
const Parse = require('parse/node');
```

For React Native applications, include `'parse/react-native'`:
For React Native applications, include `'parse/react-native.js'`:
```js
// In a React Native application
var Parse = require('parse/react-native');
const Parse = require('parse/react-native.js');

// On React Native >= 0.50 and Parse >= 1.11.0, set the Async
const AsyncStorage = require('react-native').AsyncStorage;
Parse.setAsyncStorage(AsyncStorage);
```

For WeChat miniprogram, include `'parse/weapp'`:
```js
// In a WeChat miniprogram
const Parse = require('parse/weapp');
```
If you want to use a pre-compiled file, you can fetch it from [unpkg](https://unpkg.com). The development version is available at [https://unpkg.com/parse/dist/parse.weapp.js](https://unpkg.com/parse/dist/parse.weapp.js), and the minified production version is at [https://unpkg.com/parse/dist/parse.weapp.min.js](https://unpkg.com/parse/dist/parse.weapp.min.js).

For TypeScript applications, install `'@types/parse'`:
```
$ npm install @types/parse
```

Types are updated manually after every release. If a definition doesn't exist, please submit a pull request to [@types/parse][types-parse]

## Upgrading to Parse SDK 2.0.0

With Parse SDK 2.0.0, gone are the backbone style callbacks and Parse.Promises.

We have curated a [migration guide][migration] that should help you migrate your code.

## 3rd Party Authentications

Parse Server supports many [3rd Party Authenications][3rd-party-auth]. It is possible to [linkWith][link-with] any 3rd Party Authentication by creating a [custom authentication module][custom-auth-module].

## Want to ride the bleeding edge?

We recommend using the most recent tagged build published to npm for production. However, you can test not-yet-released versions of the Parse-SDK-JS by referencing specific branches in your `package.json`. For example, to use the master branch:

```
npm install parse-community/Parse-SDK-JS.git#master
```

### Experimenting

You can also use your own forks, and work in progress branches by specifying them:

```
npm install github:myUsername/Parse-SDK-JS#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 Javascript SDK guide][contributing].

## License

```
@@ -47,13 +124,13 @@ LICENSE file in the root directory of this source tree. An additional grant
of patent rights can be found in the PATENTS file in the same directory.
```

-----
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.

[build-status-svg]: https://travis-ci.org/parse-community/Parse-SDK-JS.svg?branch=master
[build-status-link]: https://travis-ci.org/parse-community/Parse-SDK-JS
[coverage-status-svg]: http://codecov.io/github/parse-community/Parse-SDK-JS/coverage.svg?branch=master
[coverage-status-link]: http://codecov.io/github/parse-community/Parse-SDK-JS?branch=master
[npm-svg]: https://badge.fury.io/js/parse.svg
[npm-link]: https://npmjs.org/parse
[license-svg]: https://img.shields.io/badge/license-BSD-lightgrey.svg
[license-link]: https://github.com/parse-community/Parse-SDK-JS/blob/master/LICENSE

[3rd-party-auth]: http://docs.parseplatform.org/parse-server/guide/#oauth-and-3rd-party-authentication
[contributing]: https://github.com/parse-community/Parse-SDK-JS/blob/master/CONTRIBUTING.md
[custom-auth-module]: https://docs.parseplatform.org/js/guide/#custom-authentication-module
[link-with]: https://docs.parseplatform.org/js/guide/#linking-users
[migration]: https://github.com/parse-community/Parse-SDK-JS/blob/master/2.0.0.md
[open-collective-link]: https://opencollective.com/parse-server
[types-parse]: https://github.com/DefinitelyTyped/DefinitelyTyped/tree/master/types/parse
10 changes: 8 additions & 2 deletions babel-jest.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,12 @@
const babelJest = require('babel-jest');

module.exports = babelJest.createTransformer({
presets: ['es2015', 'react', 'stage-2'],
plugins: [],
presets: [["@babel/preset-env", {
"targets": {
"node": "8"
},
useBuiltIns: 'entry',
corejs: 3,
}]],
plugins: ['@babel/plugin-transform-flow-comments'],
});
66 changes: 66 additions & 0 deletions build_releases.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
const pkg = require('./package.json');
const fs = require('fs');
const path = require('path');
const { exec } = require('child_process');

const rmDir = function(dirPath) {
if (fs.existsSync(dirPath)) {
const files = fs.readdirSync(dirPath);
files.forEach(function(file) {
const curPath = path.join(dirPath, file);
if (fs.lstatSync(curPath).isDirectory()) {
rmDir(curPath);
} else {
fs.unlinkSync(curPath);
}
});
fs.rmdirSync(dirPath);
}
};

const execCommand = function(cmd) {
return new Promise((resolve, reject) => {
exec(cmd, (error, stdout, stderr) => {
if (error) {
console.warn(error);
return reject(error);
}
const output = stdout ? stdout : stderr;
console.log(output);
resolve(output);
});
});
};

console.log(`Building JavaScript SDK v${pkg.version}...\n`)

console.log('Cleaning up old builds...\n');

rmDir(path.join(__dirname, 'dist'));
rmDir(path.join(__dirname, 'lib'));

const crossEnv = 'npm run cross-env';
const gulp = 'npm run gulp';

(async function() {
console.log('Browser Release:');
console.log('Weapp Release:');
console.log('Node.js Release:');
console.log('React Native Release:');
await Promise.all([
execCommand(`${crossEnv} PARSE_BUILD=browser ${gulp} compile`),
execCommand(`${crossEnv} PARSE_BUILD=weapp ${gulp} compile`),
execCommand(`${crossEnv} PARSE_BUILD=node ${gulp} compile`),
execCommand(`${crossEnv} PARSE_BUILD=react-native ${gulp} compile`),
]);

console.log('Bundling and minifying for CDN distribution:');
await Promise.all([
execCommand(`${gulp} browserify`),
execCommand(`${gulp} browserify-weapp`),
]);
await Promise.all([
execCommand(`${gulp} minify`),
execCommand(`${gulp} minify-weapp`),
]);
}());
17 changes: 0 additions & 17 deletions build_releases.sh

This file was deleted.

118 changes: 87 additions & 31 deletions gulpfile.js
Original file line number Diff line number Diff line change
@@ -1,29 +1,46 @@
var babel = require('gulp-babel');
var browserify = require('browserify');
var derequire = require('gulp-derequire');
var gulp = require('gulp');
var insert = require('gulp-insert');
var path = require('path');
var rename = require('gulp-rename');
var replace = require('gulp-replace');
var source = require('vinyl-source-stream');
var uglify = require('gulp-uglify');
const babel = require('gulp-babel');
const browserify = require('browserify');
const derequire = require('gulp-derequire');
const gulp = require('gulp');
const insert = require('gulp-insert');
const path = require('path');
const rename = require('gulp-rename');
const source = require('vinyl-source-stream');
const uglify = require('gulp-uglify');
const watch = require('gulp-watch');

var BUILD = process.env.PARSE_BUILD || 'browser';
var VERSION = require('./package.json').version;
const BUILD = process.env.PARSE_BUILD || 'browser';
const VERSION = require('./package.json').version;

var PRESETS = {
'browser': ['es2015', 'react', 'stage-2'],
'node': ['es2015', 'react', 'stage-2'],
'react-native': ['react'],
const transformRuntime = ["@babel/plugin-transform-runtime", {
"corejs": 3,
"helpers": true,
"regenerator": true,
"useESModules": false
}];

const PRESETS = {
'browser': [["@babel/preset-env", {
"targets": "> 0.25%, not dead"
}], '@babel/preset-react'],
'weapp': [["@babel/preset-env", {
"targets": "> 0.25%, not dead"
}], '@babel/preset-react'],
'node': [["@babel/preset-env", {
"targets": { "node": "8" }
}]],
'react-native': ['module:metro-react-native-babel-preset'],
};
var PLUGINS = {
'browser': ['inline-package-json', 'transform-inline-environment-variables', 'transform-runtime'],
'node': ['inline-package-json', 'transform-inline-environment-variables', 'transform-runtime'],
'react-native': ['inline-package-json', 'transform-inline-environment-variables'],
const PLUGINS = {
'browser': [transformRuntime, '@babel/plugin-transform-flow-comments', '@babel/plugin-proposal-class-properties', 'inline-package-json',
['transform-inline-environment-variables', {'exclude': ['SERVER_RENDERING']}]],
'weapp': [transformRuntime, '@babel/plugin-transform-flow-comments', '@babel/plugin-proposal-class-properties', 'inline-package-json',
['transform-inline-environment-variables', {'exclude': ['SERVER_RENDERING']}]],
'node': ['@babel/plugin-transform-flow-comments', 'inline-package-json', 'transform-inline-environment-variables'],
'react-native': ['@babel/plugin-transform-flow-comments', 'inline-package-json', 'transform-inline-environment-variables']
};

var DEV_HEADER = (
const DEV_HEADER = (
'/**\n' +
' * Parse JavaScript SDK v' + VERSION + '\n' +
' *\n' +
@@ -32,7 +49,7 @@ var DEV_HEADER = (
' */\n'
);

var FULL_HEADER = (
const FULL_HEADER = (
'/**\n' +
' * Parse JavaScript SDK v' + VERSION + '\n' +
' *\n' +
@@ -48,9 +65,6 @@ var FULL_HEADER = (
);

gulp.task('compile', function() {
var packageJSON = {
version: VERSION
};
return gulp.src('src/*.js')
.pipe(babel({
presets: PRESETS[BUILD],
@@ -63,26 +77,68 @@ gulp.task('compile', function() {
.pipe(gulp.dest(path.join('lib', BUILD)));
});

gulp.task('browserify', function() {
var stream = browserify({
gulp.task('browserify', function(cb) {
const stream = browserify({
builtins: ['_process', 'events'],
entries: 'lib/browser/Parse.js',
standalone: 'Parse'
})
.exclude('xmlhttprequest')
.ignore('_process')
.bundle();

.exclude('xmlhttprequest')
.ignore('_process')
.bundle();
stream.on('end', () => {
cb();
});
return stream.pipe(source('parse.js'))
.pipe(derequire())
.pipe(insert.prepend(DEV_HEADER))
.pipe(gulp.dest('./dist'));
});


gulp.task('browserify-weapp', function(cb) {
const stream = browserify({
builtins: ['_process', 'events'],
entries: 'lib/weapp/Parse.js',
standalone: 'Parse'
})
.exclude('xmlhttprequest')
.ignore('_process')
.bundle();
stream.on('end', () => {
cb();
});
return stream.pipe(source('parse.weapp.js'))
.pipe(derequire())
.pipe(insert.prepend(DEV_HEADER))
.pipe(gulp.dest('./dist'));
});

gulp.task('minify', function() {
return gulp.src('dist/parse.js')
.pipe(uglify())
.pipe(insert.prepend(FULL_HEADER))
.pipe(rename({ extname: '.min.js' }))
.pipe(gulp.dest('./dist'))
});

gulp.task('minify-weapp', function() {
return gulp.src('dist/parse.weapp.js')
.pipe(uglify())
.pipe(insert.prepend(FULL_HEADER))
.pipe(rename({ extname: '.min.js' }))
.pipe(gulp.dest('./dist'))
});

gulp.task('watch', function() {
return watch('src/*.js', { ignoreInitial: false, verbose: true })
.pipe(babel({
presets: PRESETS[BUILD],
plugins: PLUGINS[BUILD],
}))
// Second pass to kill BUILD-switched code
.pipe(babel({
plugins: ['minify-dead-code-elimination'],
}))
.pipe(gulp.dest(path.join('lib', BUILD)));
});
11 changes: 0 additions & 11 deletions integration/package.json

This file was deleted.

27 changes: 0 additions & 27 deletions integration/server.js

This file was deleted.

20 changes: 20 additions & 0 deletions integration/test/.eslintrc.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
{
"env": {
"jasmine": true
},
"globals": {
"Child": true,
"Container": true,
"DiffObject": true,
"Item": true,
"Parse": true,
"Parent": true,
"reconfigureServer": true,
"TestObject": true,
"TestPoint": true
},
"rules": {
"no-console": [0],
"no-var": "error"
}
}
696 changes: 381 additions & 315 deletions integration/test/ArrayOperationsTest.js

Large diffs are not rendered by default.

13 changes: 13 additions & 0 deletions integration/test/CustomAuth.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
/* eslint-disable */
function validateAuthData(authData, options) {
return Promise.resolve({});
}

function validateAppId(appIds, authData, options) {
return Promise.resolve({});
}

module.exports = {
validateAppId,
validateAuthData,
};
370 changes: 197 additions & 173 deletions integration/test/DirtyTest.js

Large diffs are not rendered by default.

71 changes: 71 additions & 0 deletions integration/test/IdempotencyTest.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
'use strict';

const Parse = require('../../node');

const Item = Parse.Object.extend('IdempotencyItem');
const RESTController = Parse.CoreManager.getRESTController();

const XHR = RESTController._getXHR();
function DuplicateXHR(requestId) {
function XHRWrapper() {
const xhr = new XHR();
const send = xhr.send;
xhr.send = function () {
this.setRequestHeader('X-Parse-Request-Id', requestId);
send.apply(this, arguments);
};
return xhr;
}
return XHRWrapper;
}

describe('Idempotency', () => {
beforeEach(() => {
RESTController._setXHR(XHR);
});

it('handle duplicate cloud code function request', async () => {
RESTController._setXHR(DuplicateXHR('1234'));
await Parse.Cloud.run('CloudFunctionIdempotency');
await expectAsync(Parse.Cloud.run('CloudFunctionIdempotency')).toBeRejectedWithError(
'Duplicate request'
);
await expectAsync(Parse.Cloud.run('CloudFunctionIdempotency')).toBeRejectedWithError(
'Duplicate request'
);

const query = new Parse.Query(Item);
const results = await query.find();
expect(results.length).toBe(1);
});

it('handle duplicate job request', async () => {
RESTController._setXHR(DuplicateXHR('1234'));
const params = { startedBy: 'Monty Python' };
const jobStatusId = await Parse.Cloud.startJob('CloudJob1', params);
await expectAsync(Parse.Cloud.startJob('CloudJob1', params)).toBeRejectedWithError(
'Duplicate request'
);

const jobStatus = await Parse.Cloud.getJobStatus(jobStatusId);
expect(jobStatus.get('status')).toBe('succeeded');
expect(jobStatus.get('params').startedBy).toBe('Monty Python');
});

it('handle duplicate POST / PUT request', async () => {
RESTController._setXHR(DuplicateXHR('1234'));
const testObject = new Parse.Object('IdempotentTest');
await testObject.save();
await expectAsync(testObject.save()).toBeRejectedWithError('Duplicate request');

RESTController._setXHR(DuplicateXHR('5678'));
testObject.set('foo', 'bar');
await testObject.save();
await expectAsync(testObject.save()).toBeRejectedWithError('Duplicate request');

const query = new Parse.Query('IdempotentTest');
const results = await query.find();
expect(results.length).toBe(1);
expect(results[0].get('foo')).toBe('bar');
});
});
378 changes: 207 additions & 171 deletions integration/test/IncrementTest.js

Large diffs are not rendered by default.

922 changes: 421 additions & 501 deletions integration/test/ParseACLTest.js

Large diffs are not rendered by default.

154 changes: 154 additions & 0 deletions integration/test/ParseCloudTest.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
'use strict';

const assert = require('assert');
const Parse = require('../../node');
const sleep = require('./sleep');

describe('Parse Cloud', () => {
it('run function', done => {
const params = { key1: 'value2', key2: 'value1' };
Parse.Cloud.run('bar', params)
.then(result => {
assert.equal('Foo', result);
done();
})
.catch(done.fail);
});

it('run function with user', done => {
const params = { key1: 'value2', key2: 'value1' };
const user = new Parse.User();
user.setUsername('someuser');
user.setPassword('somepassword');
user
.signUp()
.then(() => {
return Parse.Cloud.run('bar', params);
})
.then(resp => {
assert.equal('Foo', resp);
return user.destroy({ useMasterKey: true });
})
.then(() => {
done();
})
.catch(done.fail);
});

it('run function failed', done => {
const params = { key1: 'value1', key2: 'value2' };
Parse.Cloud.run('bar', params)
.then(done.fail)
.catch(error => {
assert.equal(error.code, Parse.Error.SCRIPT_FAILED);
done();
});
});

it('run function name fail', done => {
const params = { key1: 'value1' };
Parse.Cloud.run('unknown_function', params)
.then(done.fail)
.catch(error => {
assert.equal(error.message, 'Invalid function: "unknown_function"');
done();
});
});

it('run function with geopoint params does not fail', done => {
const params = { key1: new Parse.GeoPoint(50, 50) };
Parse.Cloud.run('unknown_function', params)
.then(null)
.catch(error => {
assert.equal(error.message, 'Invalid function: "unknown_function"');
done();
});
});

it('run function with object params fail', done => {
const object = new Parse.Object('TestClass');
const params = { key1: object };
try {
Parse.Cloud.run('bar', params);
} catch (e) {
assert.equal(e, 'Error: Parse Objects not allowed here');
done();
}
});

it('run function with undefined', done => {
Parse.Cloud.run('CloudFunctionUndefined', {}).then(result => {
assert.strictEqual(result, undefined);
done();
});
});

it('run job', done => {
const params = { startedBy: 'Monty Python' };
Parse.Cloud.startJob('CloudJob1', params)
.then(jobStatusId => {
return Parse.Cloud.getJobStatus(jobStatusId);
})
.then(jobStatus => {
assert.equal(jobStatus.get('status'), 'succeeded');
assert.equal(jobStatus.get('params').startedBy, 'Monty Python');
done();
});
});

it('run long job', async () => {
const jobStatusId = await Parse.Cloud.startJob('CloudJob2');

let jobStatus = await Parse.Cloud.getJobStatus(jobStatusId);
assert.equal(jobStatus.get('status'), 'running');

const checkJobStatus = async () => {
const result = await Parse.Cloud.getJobStatus(jobStatusId);
return result && result.get('status') === 'succeeded';
};
while (!(await checkJobStatus())) {
await sleep(100);
}
jobStatus = await Parse.Cloud.getJobStatus(jobStatusId);
assert.equal(jobStatus.get('status'), 'succeeded');
});

it('run bad job', done => {
Parse.Cloud.startJob('bad_job')
.then(null)
.catch(error => {
assert.equal(error.code, Parse.Error.SCRIPT_FAILED);
assert.equal(error.message, 'Invalid job.');
done();
});
});

it('run failing job', done => {
Parse.Cloud.startJob('CloudJobFailing')
.then(jobStatusId => {
return Parse.Cloud.getJobStatus(jobStatusId);
})
.then(jobStatus => {
assert.equal(jobStatus.get('status'), 'failed');
assert.equal(jobStatus.get('message'), 'cloud job failed');
done();
});
});

it('get jobs data', done => {
Parse.Cloud.getJobsData().then(result => {
assert.equal(result.in_use.length, 0);
assert.equal(result.jobs.length, 3);
done();
});
});

it('invalid job status id', done => {
Parse.Cloud.getJobStatus('not-a-real-id')
.then(null)
.catch(error => {
assert.equal(error.message, 'Object not found.');
done();
});
});
});
44 changes: 44 additions & 0 deletions integration/test/ParseConfigTest.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
'use strict';

const assert = require('assert');
const Parse = require('../../node');

function testConfig() {
return Parse.Config.save({ internal: 'i', string: 's', number: 12 }, { internal: true });
}

describe('Parse Config', () => {
it('can create a config', async () => {
const config = await testConfig();

assert.notStrictEqual(config, undefined);
assert.strictEqual(config.get('string'), 's');
assert.strictEqual(config.get('internal'), 'i');
assert.strictEqual(config.get('number'), 12);
});

it('can get a config', async () => {
await testConfig();

const config = await Parse.Config.get();
assert.notStrictEqual(config, undefined);
assert.strictEqual(config.get('string'), 's');
assert.strictEqual(config.get('number'), 12);
});

it('can get internal config parameter with masterkey', async () => {
await testConfig();

const config = await Parse.Config.get({ useMasterKey: true });
assert.equal(config.get('internal'), 'i');
assert.equal(config.get('string'), 's');
});

it('cannot get internal config parameter without masterkey', async () => {
await testConfig();

const config = await Parse.Config.get();
assert.equal(config.get('internal'), undefined);
assert.equal(config.get('string'), 's');
});
});
256 changes: 256 additions & 0 deletions integration/test/ParseEventuallyQueueTest.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,256 @@
'use strict';

const assert = require('assert');
const Parse = require('../../node');
const sleep = require('./sleep');

describe('Parse EventuallyQueue', () => {
beforeEach(async () => {
await Parse.EventuallyQueue.clear();
});

it('can queue save object', async () => {
const object = new TestObject({ test: 'test' });
await object.save();
object.set('foo', 'bar');
await Parse.EventuallyQueue.save(object);
await Parse.EventuallyQueue.sendQueue();

const query = new Parse.Query(TestObject);
const result = await query.get(object.id);
assert.strictEqual(result.get('foo'), 'bar');

const length = await Parse.EventuallyQueue.length();
assert.strictEqual(length, 0);
});

it('can queue destroy object', async () => {
const object = new TestObject({ test: 'test' });
await object.save();
await Parse.EventuallyQueue.destroy(object);
await Parse.EventuallyQueue.sendQueue();

const query = new Parse.Query(TestObject);
query.equalTo('objectId', object.id);
const results = await query.find();
assert.strictEqual(results.length, 0);

const length = await Parse.EventuallyQueue.length();
assert.strictEqual(length, 0);
});

it('can queue multiple object', async () => {
const obj1 = new TestObject({ foo: 'bar' });
const obj2 = new TestObject({ foo: 'baz' });
const obj3 = new TestObject({ foo: 'bag' });
await Parse.EventuallyQueue.save(obj1);
await Parse.EventuallyQueue.save(obj2);
await Parse.EventuallyQueue.save(obj3);

let length = await Parse.EventuallyQueue.length();
assert.strictEqual(length, 3);

await Parse.EventuallyQueue.sendQueue();

const query = new Parse.Query(TestObject);
query.ascending('createdAt');
const results = await query.find();
assert.strictEqual(results.length, 3);
assert.strictEqual(results[0].get('foo'), 'bar');
assert.strictEqual(results[1].get('foo'), 'baz');
assert.strictEqual(results[2].get('foo'), 'bag');

length = await Parse.EventuallyQueue.length();
assert.strictEqual(length, 0);

// TODO: Properly handle SingleInstance
await Parse.EventuallyQueue.destroy(results[0]);
await Parse.EventuallyQueue.destroy(results[1]);
await Parse.EventuallyQueue.destroy(results[2]);

length = await Parse.EventuallyQueue.length();
assert.strictEqual(length, 3);

await Parse.EventuallyQueue.sendQueue();
const objects = await query.find();
assert.strictEqual(objects.length, 0);
});

it('can queue destroy for object that does not exist', async () => {
const object = new TestObject({ test: 'test' });
await object.save();
await object.destroy();
await Parse.EventuallyQueue.destroy(object);
await Parse.EventuallyQueue.sendQueue();

const length = await Parse.EventuallyQueue.length();
assert.strictEqual(length, 0);
});

it('can queue destroy then save', async () => {
const object = new TestObject({ hash: 'test' });
await Parse.EventuallyQueue.destroy(object);
await Parse.EventuallyQueue.save(object);
await Parse.EventuallyQueue.sendQueue();

const query = new Parse.Query(TestObject);
query.equalTo('hash', 'test');
const results = await query.find();
assert.strictEqual(results.length, 1);

const length = await Parse.EventuallyQueue.length();
assert.strictEqual(length, 0);
});

it('can queue unsaved object with hash', async () => {
const hash = 'secret';
const object = new TestObject({ test: 'test' });
object.set('hash', hash);
await Parse.EventuallyQueue.save(object);
await Parse.EventuallyQueue.sendQueue();

const query = new Parse.Query(TestObject);
query.equalTo('hash', hash);
const results = await query.find();
assert.strictEqual(results.length, 1);
});

it('can queue saved object and unsaved with hash', async () => {
const hash = 'ransom+salt';
const object = new TestObject({ test: 'test' });
object.set('hash', hash);
await Parse.EventuallyQueue.save(object);
await Parse.EventuallyQueue.sendQueue();

let query = new Parse.Query(TestObject);
query.equalTo('hash', hash);
const results = await query.find();
assert.strictEqual(results.length, 1);

const unsaved = new TestObject({ hash, foo: 'bar' });
await Parse.EventuallyQueue.save(unsaved);
await Parse.EventuallyQueue.sendQueue();

query = new Parse.Query(TestObject);
query.equalTo('hash', hash);
const hashes = await query.find();
assert.strictEqual(hashes.length, 1);
assert.strictEqual(hashes[0].get('foo'), 'bar');
});

it('can queue same object but override undefined fields', async () => {
const object = new Parse.Object('TestObject');
object.set('foo', 'bar');
object.set('test', '1234');
await Parse.EventuallyQueue.save(object);

object.set('foo', undefined);
await Parse.EventuallyQueue.save(object);

const length = await Parse.EventuallyQueue.length();
assert.strictEqual(length, 1);

const queue = await Parse.EventuallyQueue.getQueue();
assert.strictEqual(queue[0].object.foo, 'bar');
assert.strictEqual(queue[0].object.test, '1234');
});

it('can poll server', async () => {
const object = new TestObject({ test: 'test' });
await object.save();
object.set('foo', 'bar');
await Parse.EventuallyQueue.save(object);
Parse.EventuallyQueue.poll();
assert.ok(Parse.EventuallyQueue.isPolling());

while (Parse.EventuallyQueue.isPolling()) {
await sleep(100);
}
const query = new Parse.Query(TestObject);
let result = await query.get(object.id);
while (result.get('foo') !== 'bar') {
result = await query.get(object.id);
}
assert.strictEqual(result.get('foo'), 'bar');

const length = await Parse.EventuallyQueue.length();
assert.strictEqual(length, 0);
assert.strictEqual(Parse.EventuallyQueue.isPolling(), false);
});

it('can clear queue', async () => {
const object = new TestObject({ test: 'test' });
await object.save();
await Parse.EventuallyQueue.save(object);
const q = await Parse.EventuallyQueue.getQueue();
assert.strictEqual(q.length, 1);

await Parse.EventuallyQueue.clear();
const length = await Parse.EventuallyQueue.length();
assert.strictEqual(length, 0);
});

it('can saveEventually', async done => {
const parseServer = await reconfigureServer();
const object = new TestObject({ hash: 'saveSecret' });
parseServer.server.close(async () => {
await object.saveEventually();
let length = await Parse.EventuallyQueue.length();
assert(Parse.EventuallyQueue.isPolling());
assert.strictEqual(length, 1);

await reconfigureServer({});
while (Parse.EventuallyQueue.isPolling()) {
await sleep(100);
}
assert.strictEqual(Parse.EventuallyQueue.isPolling(), false);

while (await Parse.EventuallyQueue.length()) {
await sleep(100);
}
length = await Parse.EventuallyQueue.length();
assert.strictEqual(length, 0);

const query = new Parse.Query(TestObject);
query.equalTo('hash', 'saveSecret');
let results = await query.find();
while (results.length === 0) {
results = await query.find();
}
assert.strictEqual(results.length, 1);
done();
});
});

it('can destroyEventually', async done => {
const parseServer = await reconfigureServer();
const object = new TestObject({ hash: 'deleteSecret' });
await object.save();
parseServer.server.close(async () => {
await object.destroyEventually();
let length = await Parse.EventuallyQueue.length();
assert(Parse.EventuallyQueue.isPolling());
assert.strictEqual(length, 1);

await reconfigureServer({});
while (Parse.EventuallyQueue.isPolling()) {
await sleep(100);
}
assert.strictEqual(Parse.EventuallyQueue.isPolling(), false);
while (await Parse.EventuallyQueue.length()) {
await sleep(100);
}
length = await Parse.EventuallyQueue.length();
assert.strictEqual(length, 0);

const query = new Parse.Query(TestObject);
query.equalTo('hash', 'deleteSecret');
let results = await query.find();
while (results.length) {
results = await query.find();
}
assert.strictEqual(results.length, 0);
done();
});
});
});
115 changes: 115 additions & 0 deletions integration/test/ParseFileTest.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
'use strict';

const assert = require('assert');
const Parse = require('../../node');

describe('Parse.File', () => {
it('can save file with uri', async () => {
// Try https
const parseLogo =
'https://raw.githubusercontent.com/parse-community/parse-server/master/.github/parse-server-logo.png';
const file1 = new Parse.File('parse-server-logo', { uri: parseLogo });
await file1.save();

const object = new Parse.Object('TestObject');
object.set('file1', file1);
await object.save();

const query = new Parse.Query('TestObject');
let result = await query.get(object.id);

assert.equal(file1.name(), result.get('file1').name());
assert.equal(file1.url(), result.get('file1').url());

// Try http
const file2 = new Parse.File('parse-server-logo', { uri: file1.url() });
await file2.save();

object.set('file2', file2);
await object.save();

result = await query.get(object.id);
assert.equal(file2.url(), result.get('file2').url());
});

it('can cancel save file with uri', async () => {
const parseLogo =
'https://raw.githubusercontent.com/parse-community/parse-server/master/.github/parse-server-logo.png';
const file = new Parse.File('parse-server-logo', { uri: parseLogo });
file.save().then(() => {
assert.equal(file.name(), undefined);
assert.equal(file.url(), undefined);
});
file.cancel();
});

it('can not get data from unsaved file', async () => {
const file = new Parse.File('parse-server-logo', [61, 170, 236, 120]);
file._data = null;
try {
await file.getData();
} catch (e) {
assert.equal(e.message, 'Cannot retrieve data for unsaved ParseFile.');
}
});

it('can get file data from byte array', async () => {
const file = new Parse.File('parse-server-logo', [61, 170, 236, 120]);
let data = await file.getData();
assert.equal(data, 'ParseA==');
file._data = null;
await file.save();
assert.equal(file._data, null);
data = await file.getData();
assert.equal(data, 'ParseA==');
});

it('can get file data from base64', async () => {
const file = new Parse.File('parse-server-logo', { base64: 'ParseA==' });
let data = await file.getData();
assert.equal(data, 'ParseA==');
file._data = null;
await file.save();
assert.equal(file._data, null);
data = await file.getData();
assert.equal(data, 'ParseA==');
});

it('can get file data from full base64', async () => {
const file = new Parse.File('parse-server-logo', {
base64: 'data:image/jpeg;base64,ParseA==',
});
let data = await file.getData();
assert.equal(data, 'ParseA==');
file._data = null;
await file.save();
assert.equal(file._data, null);
data = await file.getData();
assert.equal(data, 'ParseA==');
});

it('can delete file', async () => {
const parseLogo =
'https://raw.githubusercontent.com/parse-community/parse-server/master/.github/parse-server-logo.png';
const file = new Parse.File('parse-server-logo', { uri: parseLogo });
await file.save();
const data = await file.getData();

const deletedFile = await file.destroy();
const deletedData = await file.getData();
assert.equal(file, deletedFile);
assert.notEqual(data, deletedData);
});

it('can handle delete file error', async () => {
const parseLogo =
'https://raw.githubusercontent.com/parse-community/parse-server/master/.github/parse-server-logo.png';
const file = new Parse.File('parse-server-logo', { uri: parseLogo });
try {
await file.destroy();
assert.equal(false, true);
} catch (e) {
assert.equal(e.code, Parse.Error.FILE_DELETE_ERROR);
}
});
});
157 changes: 75 additions & 82 deletions integration/test/ParseGeoBoxTest.js
Original file line number Diff line number Diff line change
@@ -1,119 +1,112 @@
'use strict';

const assert = require('assert');
const clear = require('./clear');
const mocha = require('mocha');
const Parse = require('../../node');

describe('Geo Box', () => {
before(() => {
Parse.initialize('integration');
Parse.CoreManager.set('SERVER_URL', 'http://localhost:1337/parse');
Parse.Storage._clear();
});

beforeEach((done) => {
clear().then(() => {
Parse.User.logOut().then(() => { done() }, () => { done() });
});
});
const southwestOfSF = new Parse.GeoPoint(37.708813, -122.526398);
const northeastOfSF = new Parse.GeoPoint(37.822802, -122.373962);

it('can query geo boxes', () => {
let caltrainStationLocation = new Parse.GeoPoint(37.776346, -122.394218);
let caltrainStation = new Parse.Object('Location');
describe('Geo Box', () => {
it('can query geo boxes', done => {
const caltrainStationLocation = new Parse.GeoPoint(37.776346, -122.394218);
const caltrainStation = new Parse.Object('Location');
caltrainStation.set('location', caltrainStationLocation);
caltrainStation.set('name', 'caltrain');

let santaClaraLocation = new Parse.GeoPoint(37.325635, -121.945753);
let santaClara = new Parse.Object('Location');
const santaClaraLocation = new Parse.GeoPoint(37.325635, -121.945753);
const santaClara = new Parse.Object('Location');
santaClara.set('location', santaClaraLocation);
santaClara.set('name', 'santa clara');

let southwestOfSF = new Parse.GeoPoint(37.708813, -122.526398);
let northeastOfSF = new Parse.GeoPoint(37.822802, -122.373962);

Parse.Object.saveAll([caltrainStation, santaClara]).then(() => {
let query = new Parse.Query('Location');
query.withinGeoBox('location', southwestOfSF, northeastOfSF);
return query.find();
}).then((objectsInSF) => {
assert.equal(objectsInSF, 1);
assert.equal(objectsInSF[0].get('name'), 'caltrain');
done();
});
Parse.Object.saveAll([caltrainStation, santaClara])
.then(() => {
const query = new Parse.Query('Location');
query.withinGeoBox('location', southwestOfSF, northeastOfSF);
return query.find();
})
.then(objectsInSF => {
assert.equal(objectsInSF.length, 1);
assert.equal(objectsInSF[0].get('name'), 'caltrain');
done();
})
.catch(done.fail);
});

it('can swap geo box corners', () => {
let caltrainStationLocation = new Parse.GeoPoint(37.776346, -122.394218);
let caltrainStation = new Parse.Object('Location');
it('can swap geo box corners', done => {
const caltrainStationLocation = new Parse.GeoPoint(37.776346, -122.394218);
const caltrainStation = new Parse.Object('Location');
caltrainStation.set('location', caltrainStationLocation);
caltrainStation.set('name', 'caltrain');

let santaClaraLocation = new Parse.GeoPoint(37.325635, -121.945753);
let santaClara = new Parse.Object('Location');
const santaClaraLocation = new Parse.GeoPoint(37.325635, -121.945753);
const santaClara = new Parse.Object('Location');
santaClara.set('location', santaClaraLocation);
santaClara.set('name', 'santa clara');

let southwestOfSF = new Parse.GeoPoint(37.708813, -122.526398);
let northeastOfSF = new Parse.GeoPoint(37.822802, -122.373962);

Parse.Object.saveAll([caltrainStation, santaClara]).then(() => {
let query = new Parse.Query('Location');
query.withinGeoBox('location', northeastOfSF, southwestOfSF);
return query.find();
}).fail(() => {
// Query should fail for crossing the date line
done();
});
const southwestOfSF = new Parse.GeoPoint(37.708813, -122.526398);
const northeastOfSF = new Parse.GeoPoint(37.822802, -122.373962);

Parse.Object.saveAll([caltrainStation, santaClara])
.then(() => {
const query = new Parse.Query('Location');
query.withinGeoBox('location', northeastOfSF, southwestOfSF);
return query.find();
})
.then(objectsInSF => {
assert.equal(objectsInSF.length, 1);
assert.equal(objectsInSF[0].get('name'), 'caltrain');
done();
})
.catch(done.fail);
});

it('can swap longitude', () => {
let caltrainStationLocation = new Parse.GeoPoint(37.776346, -122.394218);
let caltrainStation = new Parse.Object('Location');
it('can swap longitude', done => {
const caltrainStationLocation = new Parse.GeoPoint(37.776346, -122.394218);
const caltrainStation = new Parse.Object('Location');
caltrainStation.set('location', caltrainStationLocation);
caltrainStation.set('name', 'caltrain');

let santaClaraLocation = new Parse.GeoPoint(37.325635, -121.945753);
let santaClara = new Parse.Object('Location');
const santaClaraLocation = new Parse.GeoPoint(37.325635, -121.945753);
const santaClara = new Parse.Object('Location');
santaClara.set('location', santaClaraLocation);
santaClara.set('name', 'santa clara');

let northwestOfSF = new Parse.GeoPoint(37.822802, -122.526398);
let southeastOfSF = new Parse.GeoPoint(37.708813, -122.373962);

Parse.Object.saveAll([caltrainStation, santaClara]).then(() => {
let query = new Parse.Query('Location');
query.withinGeoBox('location', southwestOfSF, northeastOfSF);
return query.find();
}).then((objectsInSF) => {
assert.equal(objectsInSF, 1);
assert.equal(objectsInSF[0].get('name'), 'caltrain');
done();
});
Parse.Object.saveAll([caltrainStation, santaClara])
.then(() => {
const query = new Parse.Query('Location');
query.withinGeoBox('location', southwestOfSF, northeastOfSF);
return query.find();
})
.then(objectsInSF => {
assert.equal(objectsInSF.length, 1);
assert.equal(objectsInSF[0].get('name'), 'caltrain');
done();
})
.catch(done.fail);
});

it('can swap latitude', () => {
let caltrainStationLocation = new Parse.GeoPoint(37.776346, -122.394218);
let caltrainStation = new Parse.Object('Location');
it('can swap latitude', done => {
const caltrainStationLocation = new Parse.GeoPoint(37.776346, -122.394218);
const caltrainStation = new Parse.Object('Location');
caltrainStation.set('location', caltrainStationLocation);
caltrainStation.set('name', 'caltrain');

let santaClaraLocation = new Parse.GeoPoint(37.325635, -121.945753);
let santaClara = new Parse.Object('Location');
const santaClaraLocation = new Parse.GeoPoint(37.325635, -121.945753);
const santaClara = new Parse.Object('Location');
santaClara.set('location', santaClaraLocation);
santaClara.set('name', 'santa clara');

let northwestOfSF = new Parse.GeoPoint(37.822802, -122.526398);
let southeastOfSF = new Parse.GeoPoint(37.708813, -122.373962);

Parse.Object.saveAll([caltrainStation, santaClara]).then(() => {
let query = new Parse.Query('Location');
query.withinGeoBox('location', southwestOfSF, northeastOfSF);
return query.find();
}).then((objectsInSF) => {
assert.equal(objectsInSF, 1);
assert.equal(objectsInSF[0].get('name'), 'caltrain');
done();
});
Parse.Object.saveAll([caltrainStation, santaClara])
.then(() => {
const query = new Parse.Query('Location');
query.withinGeoBox('location', southwestOfSF, northeastOfSF);
return query.find();
})
.then(objectsInSF => {
assert.equal(objectsInSF.length, 1);
assert.equal(objectsInSF[0].get('name'), 'caltrain');
done();
})
.catch(done.fail);
});
});
});
517 changes: 353 additions & 164 deletions integration/test/ParseGeoPointTest.js

Large diffs are not rendered by default.

259 changes: 259 additions & 0 deletions integration/test/ParseLiveQueryTest.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,259 @@
'use strict';

const assert = require('assert');
const Parse = require('../../node');
const sleep = require('./sleep');

describe('Parse LiveQuery', () => {
beforeEach(() => {
Parse.User.enableUnsafeCurrentUser();
});

it('can subscribe to query', async done => {
const object = new TestObject();
await object.save();
const installationId = await Parse.CoreManager.getInstallationController().currentInstallationId();

const query = new Parse.Query(TestObject);
query.equalTo('objectId', object.id);
const subscription = await query.subscribe();

subscription.on('update', (object, original, response) => {
assert.equal(object.get('foo'), 'bar');
assert.equal(response.installationId, installationId);
done();
});
object.set({ foo: 'bar' });
await object.save();
});

it('can subscribe to query with client', async done => {
const object = new TestObject();
await object.save();
const installationId = await Parse.CoreManager.getInstallationController().currentInstallationId();

const query = new Parse.Query(TestObject);
query.equalTo('objectId', object.id);
const client = await Parse.CoreManager.getLiveQueryController().getDefaultLiveQueryClient();
if (client.shouldOpen()) {
client.open();
}
const subscription = client.subscribe(query);

subscription.on('update', (object, original, response) => {
assert.equal(object.get('foo'), 'bar');
assert.equal(response.installationId, installationId);
done();
});
await subscription.subscribePromise;
object.set({ foo: 'bar' });
await object.save();
});

it('can subscribe to query with null connect fields', async done => {
const client = new Parse.LiveQueryClient({
applicationId: 'integration',
serverURL: 'ws://localhost:1337',
javascriptKey: null,
masterKey: null,
sessionToken: null,
installationId: null,
});
client.open();
const object = new TestObject();
await object.save();

const query = new Parse.Query(TestObject);
query.equalTo('objectId', object.id);
const subscription = await client.subscribe(query);
subscription.on('update', async object => {
assert.equal(object.get('foo'), 'bar');
client.close();
done();
});
await subscription.subscribePromise;
object.set({ foo: 'bar' });
await object.save();
});

it('can subscribe to multiple queries', async () => {
const objectA = new TestObject();
const objectB = new TestObject();
await Parse.Object.saveAll([objectA, objectB]);

const queryA = new Parse.Query(TestObject);
const queryB = new Parse.Query(TestObject);
queryA.equalTo('objectId', objectA.id);
queryB.equalTo('objectId', objectB.id);
const subscriptionA = await queryA.subscribe();
const subscriptionB = await queryB.subscribe();
let count = 0;
subscriptionA.on('update', object => {
count++;
assert.equal(object.get('foo'), 'bar');
});
subscriptionB.on('update', object => {
count++;
assert.equal(object.get('foo'), 'baz');
});
await objectA.save({ foo: 'bar' });
await objectB.save({ foo: 'baz' });
await sleep(1000);
assert.equal(count, 2);
});

it('can subscribe to multiple queries different class', async () => {
const objectA = new TestObject();
const objectB = new DiffObject();
await Parse.Object.saveAll([objectA, objectB]);

const queryA = new Parse.Query(TestObject);
const queryB = new Parse.Query(DiffObject);
queryA.equalTo('objectId', objectA.id);
queryB.equalTo('objectId', objectB.id);
const subscriptionA = await queryA.subscribe();
const subscriptionB = await queryB.subscribe();
let count = 0;
subscriptionA.on('update', object => {
count++;
assert.equal(object.get('foo'), 'bar');
});
subscriptionB.on('update', object => {
count++;
assert.equal(object.get('foo'), 'baz');
});
await objectA.save({ foo: 'bar' });
await objectB.save({ foo: 'baz' });
await sleep(1000);
assert.equal(count, 2);
});

it('can unsubscribe to multiple queries different class', async () => {
const objectA = new TestObject();
const objectB = new DiffObject();
await Parse.Object.saveAll([objectA, objectB]);

const queryA = new Parse.Query(TestObject);
const queryB = new Parse.Query(DiffObject);
queryA.equalTo('objectId', objectA.id);
queryB.equalTo('objectId', objectB.id);
const subscriptionA = await queryA.subscribe();
const subscriptionB = await queryB.subscribe();
let count = 0;
subscriptionA.on('update', () => {
count++;
});
subscriptionB.on('update', object => {
count++;
assert.equal(object.get('foo'), 'baz');
});
subscriptionA.unsubscribe();
await objectA.save({ foo: 'bar' });
await objectB.save({ foo: 'baz' });
await sleep(1000);
assert.equal(count, 1);
});

it('can unsubscribe with await to multiple queries different class', async () => {
const objectA = new TestObject();
const objectB = new DiffObject();
await Parse.Object.saveAll([objectA, objectB]);

const queryA = new Parse.Query(TestObject);
const queryB = new Parse.Query(DiffObject);
queryA.equalTo('objectId', objectA.id);
queryB.equalTo('objectId', objectB.id);
const subscriptionA = await queryA.subscribe();
const subscriptionB = await queryB.subscribe();
let count = 0;
subscriptionA.on('update', () => {
count++;
});
subscriptionB.on('update', object => {
count++;
assert.equal(object.get('foo'), 'baz');
});
await subscriptionA.unsubscribe();
await objectA.save({ foo: 'bar' });
await objectB.save({ foo: 'baz' });
await sleep(1000);
assert.equal(count, 1);
});

it('can subscribe to ACL', async done => {
const user = await Parse.User.signUp('ooo', 'password');
const ACL = new Parse.ACL(user);

const object = new TestObject();
object.setACL(ACL);
await object.save();

const query = new Parse.Query(TestObject);
query.equalTo('objectId', object.id);
const subscription = await query.subscribe(user.getSessionToken());
subscription.on('update', async object => {
assert.equal(object.get('foo'), 'bar');
await Parse.User.logOut();
done();
});
await object.save({ foo: 'bar' });
});

it('can subscribe to null sessionToken', async done => {
const user = await Parse.User.signUp('oooooo', 'password');

const readOnly = Parse.User.readOnlyAttributes();
Parse.User.readOnlyAttributes = null;
user.set('sessionToken', null);
assert.equal(user.getSessionToken(), null);

const object = new TestObject();
await object.save();

const query = new Parse.Query(TestObject);
query.equalTo('objectId', object.id);
const subscription = await query.subscribe();
subscription.on('update', async object => {
assert.equal(object.get('foo'), 'bar');
Parse.User.readOnlyAttributes = function () {
return readOnly;
};
await Parse.User.logOut();
done();
});
await object.save({ foo: 'bar' });
});

it('can subscribe with open event', async done => {
const object = new TestObject();
await object.save();

const query = new Parse.Query(TestObject);
query.equalTo('objectId', object.id);
const subscription = await query.subscribe();
subscription.on('open', response => {
assert(response.clientId);
assert(response.installationId);
done();
});
});

it('can subscribe to query with fields', async done => {
const object = new TestObject();
await object.save({ name: 'hello', age: 21 });

const query = new Parse.Query(TestObject);
query.equalTo('objectId', object.id);
query.select(['name']);
const subscription = await query.subscribe();

subscription.on('update', object => {
assert.equal(object.get('name'), 'hello');
assert.equal(object.get('age'), undefined);
assert.equal(object.get('foo'), undefined);
done();
});
object.set({ foo: 'bar' });
await object.save();
});
});
2,890 changes: 2,890 additions & 0 deletions integration/test/ParseLocalDatastoreTest.js

Large diffs are not rendered by default.

47 changes: 15 additions & 32 deletions integration/test/ParseMasterKeyTest.js
Original file line number Diff line number Diff line change
@@ -1,51 +1,34 @@
'use strict';

const assert = require('assert');
const clear = require('./clear');
const mocha = require('mocha');
const Parse = require('../../node');

const TestObject = Parse.Object.extend('TestObject');

describe('Master Key', () => {
before((done) => {
Parse.initialize('integration', null, 'notsosecret');
Parse.CoreManager.set('SERVER_URL', 'http://localhost:1337/parse');
Parse.Storage._clear();
clear().then(() => {
done();
});
});

it('can perform a simple save', (done) => {
let object = new TestObject();
it('can perform a simple save', done => {
const object = new TestObject();
object.set('color', 'purple');
object.save(null, { useMasterKey: true }).then((obj) => {
object.save(null, { useMasterKey: true }).then(() => {
assert(object.id);
done();
});
});

it('can perform a save without permissions', (done) => {
let object;
Parse.User.signUp('andrew', 'password').then((user) => {
object = new TestObject({ ACL: new Parse.ACL(user) });
return object.save();
}).then(() => {
Parse.User.logOut();
return object.save(null, { useMasterKey: true });
}).then(() => {
// expect success
done();
}).fail((e) => console.log(e));
it('can perform a save without permissions', async () => {
const user = await Parse.User.signUp('andrew', 'password');
const object = new TestObject({ ACL: new Parse.ACL(user) });
await object.save();

await Parse.User.logOut();
await object.save(null, { useMasterKey: true });
});

it('throws when no master key is provided', (done) => {
it('throws when no master key is provided', done => {
Parse.CoreManager.set('MASTER_KEY', null);
let object = new TestObject();
object.save(null, { useMasterKey: true }).fail(() => {
const object = new TestObject();
object.save(null, { useMasterKey: true }).catch(() => {
// should fail
Parse.CoreManager.set('MASTER_KEY', 'notsosecret');
done();
});
});
});
});
2,656 changes: 1,700 additions & 956 deletions integration/test/ParseObjectTest.js

Large diffs are not rendered by default.

224 changes: 138 additions & 86 deletions integration/test/ParsePolygonTest.js
Original file line number Diff line number Diff line change
@@ -1,97 +1,112 @@
const assert = require('assert');
const clear = require('./clear');
const Parse = require('../../node');

const TestObject = Parse.Object.extend('TestObject');

describe('Polygon', () => {
before(() => {
Parse.initialize('integration');
Parse.CoreManager.set('SERVER_URL', 'http://localhost:1337/parse');
Parse.Storage._clear();
});

beforeEach((done) => {
clear().then(() => {
done();
});
});

it('can save polygon with points', (done) => {
const openPoints = [[0,0], [0,1], [1,1], [1,0]];
const closedPoints = [[0,0], [0,1], [1,1], [1,0], [0,0]];
it('can save polygon with points', done => {
const openPoints = [
[0, 0],
[0, 1],
[1, 1],
[1, 0],
];
const closedPoints = [
[0, 0],
[0, 1],
[1, 1],
[1, 0],
[0, 0],
];
const polygon = new Parse.Polygon(openPoints);
const obj = new TestObject({ polygon });
obj.save().then(() => {
const query = new Parse.Query(TestObject);
query.equalTo('polygon', polygon);
return query.find();
}).then((results) => {
assert.equal(results.length, 1);
assert.deepEqual(results[0].get('polygon').coordinates, closedPoints);
const closedPolygon = new Parse.Polygon(closedPoints);
const query = new Parse.Query(TestObject);
query.equalTo('polygon', closedPolygon);
return query.find();
}).then((results) => {
assert.equal(results.length, 1);
assert.deepEqual(results[0].get('polygon').coordinates, closedPoints);
done();
}, done.fail);
obj
.save()
.then(() => {
const query = new Parse.Query(TestObject);
query.equalTo('polygon', polygon);
return query.find();
})
.then(results => {
assert.equal(results.length, 1);
assert.deepEqual(results[0].get('polygon').coordinates, closedPoints);
const closedPolygon = new Parse.Polygon(closedPoints);
const query = new Parse.Query(TestObject);
query.equalTo('polygon', closedPolygon);
return query.find();
})
.then(results => {
assert.equal(results.length, 1);
assert.deepEqual(results[0].get('polygon').coordinates, closedPoints);
done();
}, done.fail);
});

it('can save polygon with GeoPoints', (done) => {
it('can save polygon with GeoPoints', done => {
const p1 = new Parse.GeoPoint(0, 0);
const p2 = new Parse.GeoPoint(0, 1);
const p3 = new Parse.GeoPoint(1, 1);
const p4 = new Parse.GeoPoint(1, 0);
const p5 = new Parse.GeoPoint(0, 0);
const closedPoints = [[0,0], [0,1], [1,1], [1,0], [0,0]];
const closedPoints = [
[0, 0],
[0, 1],
[1, 1],
[1, 0],
[0, 0],
];
const polygon = new Parse.Polygon([p1, p2, p3, p4, p5]);
const obj = new TestObject({ polygon });
obj.save().then(() => {
const query = new Parse.Query(TestObject);
query.equalTo('polygon', polygon);
return query.find();
}).then((results) => {
assert.equal(results.length, 1);
assert.deepEqual(results[0].get('polygon').coordinates, closedPoints);
const closedPolygon = new Parse.Polygon(closedPoints);
const query = new Parse.Query(TestObject);
query.equalTo('polygon', closedPolygon);
return query.find();
}).then((results) => {
assert.deepEqual(results[0].get('polygon').coordinates, closedPoints);
done();
}, done.fail);
obj
.save()
.then(() => {
const query = new Parse.Query(TestObject);
query.equalTo('polygon', polygon);
return query.find();
})
.then(results => {
assert.equal(results.length, 1);
assert.deepEqual(results[0].get('polygon').coordinates, closedPoints);
const closedPolygon = new Parse.Polygon(closedPoints);
const query = new Parse.Query(TestObject);
query.equalTo('polygon', closedPolygon);
return query.find();
})
.then(results => {
assert.deepEqual(results[0].get('polygon').coordinates, closedPoints);
done();
}, done.fail);
});

it('fail save with 3 point minumum', (done) => {
it('fail save with 3 point minumum', done => {
try {
const polygon = new Parse.Polygon([[0, 0]]);
new Parse.Polygon([[0, 0]]);
} catch (e) {
done();
}
});

it('fail save with non array', (done) => {
it('fail save with non array', done => {
try {
const polygon = new Parse.Polygon(123);
new Parse.Polygon(123);
} catch (e) {
done();
}
});

it('fail save with invalid array', (done) => {
it('fail save with invalid array', done => {
try {
const polygon = new Parse.Polygon([['str1'], ['str2'], ['str3']]);
new Parse.Polygon([['str1'], ['str2'], ['str3']]);
} catch (e) {
done();
}
});

it('containsPoint', (done) => {
const points = [[0,0], [0,1], [1,1], [1,0]];
it('containsPoint', done => {
const points = [
[0, 0],
[0, 1],
[1, 1],
[1, 0],
];
const inside = new Parse.GeoPoint(0.5, 0.5);
const outside = new Parse.GeoPoint(10, 10);
const polygon = new Parse.Polygon(points);
@@ -101,9 +116,19 @@ describe('Polygon', () => {
done();
});

it('equality', (done) => {
const points = [[0,0], [0,1], [1,1], [1,0]];
const diff = [[0,0], [0,2], [2,2], [2,0]];
it('equality', done => {
const points = [
[0, 0],
[0, 1],
[1, 1],
[1, 0],
];
const diff = [
[0, 0],
[0, 2],
[2, 2],
[2, 0],
];

const polygonA = new Parse.Polygon(points);
const polygonB = new Parse.Polygon(points);
@@ -119,10 +144,26 @@ describe('Polygon', () => {
done();
});

it('supports polygonContains', (done) => {
const p1 = [[0,0], [0,1], [1,1], [1,0]];
const p2 = [[0,0], [0,2], [2,2], [2,0]];
const p3 = [[10,10], [10,15], [15,15], [15,10], [10,10]];
it('supports polygonContains', done => {
const p1 = [
[0, 0],
[0, 1],
[1, 1],
[1, 0],
];
const p2 = [
[0, 0],
[0, 2],
[2, 2],
[2, 0],
];
const p3 = [
[10, 10],
[10, 15],
[15, 15],
[15, 10],
[10, 10],
];

const polygon1 = new Parse.Polygon(p1);
const polygon2 = new Parse.Polygon(p2);
@@ -132,29 +173,40 @@ describe('Polygon', () => {
const obj2 = new TestObject({ polygon: polygon2 });
const obj3 = new TestObject({ polygon: polygon3 });

Parse.Object.saveAll([obj1, obj2, obj3]).then(() => {
const point = new Parse.GeoPoint(0.5, 0.5);
const query = new Parse.Query(TestObject);
query.polygonContains('polygon', point);
return query.find();
}).then((results) => {
assert.equal(results.length, 2);
done();
}, done.fail);
Parse.Object.saveAll([obj1, obj2, obj3])
.then(() => {
const point = new Parse.GeoPoint(0.5, 0.5);
const query = new Parse.Query(TestObject);
query.polygonContains('polygon', point);
return query.find();
})
.then(results => {
assert.equal(results.length, 2);
done();
}, done.fail);
});

it('polygonContains invalid input', (done) => {
const points = [[0,0], [0,1], [1,1], [1,0]];
it('polygonContains invalid input', done => {
const points = [
[0, 0],
[0, 1],
[1, 1],
[1, 0],
];
const polygon = new Parse.Polygon(points);
const obj = new TestObject({ polygon });
obj.save().then(() => {
const query = new Parse.Query(TestObject);
query.polygonContains('polygon', 1234);
return query.find();
}).then(() => {
fail();
}).catch(() => {
done();
});
obj
.save()
.then(() => {
const query = new Parse.Query(TestObject);
query.polygonContains('polygon', 1234);
return query.find();
})
.then(() => {
fail();
})
.catch(() => {
done();
});
});
});
15 changes: 15 additions & 0 deletions integration/test/ParsePushTest.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
'use strict';

const Parse = require('../../node');

describe('Parse Push', () => {
it('can get pushStatusId', async () => {
const payload = {
data: { alert: 'We return status!' },
where: { deviceType: { $eq: 'random' } },
};
const pushStatusId = await Parse.Push.send(payload, { useMasterKey: true });
const pushStatus = await Parse.Push.getPushStatus(pushStatusId, { useMasterKey: true });
expect(pushStatus.id).toBe(pushStatusId);
});
});
121 changes: 85 additions & 36 deletions integration/test/ParseQueryAggregateTest.js
Original file line number Diff line number Diff line change
@@ -1,52 +1,38 @@
'use strict';

const assert = require('assert');
const clear = require('./clear');
const mocha = require('mocha');
const Parse = require('../../node');

const TestObject = Parse.Object.extend('TestObject');

describe('Parse Aggregate Query', () => {
before((done) => {
Parse.initialize('integration', null, 'notsosecret');
Parse.CoreManager.set('SERVER_URL', 'http://localhost:1337/parse');
Parse.Storage._clear();
clear().then(() => {
const obj1 = new TestObject({score: 10, name: 'foo'});
const obj2 = new TestObject({score: 10, name: 'foo'});
const obj3 = new TestObject({score: 10, name: 'bar'});
const obj4 = new TestObject({score: 20, name: 'dpl'});
return Parse.Object.saveAll([obj1, obj2, obj3, obj4]);
}).then(() => {
return Parse.User.logOut();
})
.then(() => { done() }, () => { done() });
beforeEach(async () => {
const obj1 = new TestObject({ score: 10, name: 'foo' });
const obj2 = new TestObject({ score: 10, name: 'foo' });
const obj3 = new TestObject({ score: 10, name: 'bar' });
const obj4 = new TestObject({ score: 20, name: 'dpl' });
await Parse.Object.saveAll([obj1, obj2, obj3, obj4]);
});

it('aggregate pipeline object query', (done) => {
it('aggregate pipeline object query', done => {
const pipeline = {
group: { objectId: '$name' }
group: { objectId: '$name' },
};
const query = new Parse.Query(TestObject);
query.aggregate(pipeline).then((results) => {
query.aggregate(pipeline).then(results => {
assert.equal(results.length, 3);
done();
});
});

it('aggregate pipeline array query', (done) => {
const pipeline = [
{ group: { objectId: '$name' } }
];
it('aggregate pipeline array query', done => {
const pipeline = [{ group: { objectId: '$name' } }];
const query = new Parse.Query(TestObject);
query.aggregate(pipeline).then((results) => {
query.aggregate(pipeline).then(results => {
assert.equal(results.length, 3);
done();
});
});

it('aggregate pipeline invalid query', (done) => {
it('aggregate pipeline invalid query', done => {
const pipeline = 1234;
const query = new Parse.Query(TestObject);
try {
@@ -56,20 +42,83 @@ describe('Parse Aggregate Query', () => {
}
});

it('distinct query', (done) => {
it('aggregate allow multiple of same stage', async () => {
const pointer1 = new TestObject({ value: 1 });
const pointer2 = new TestObject({ value: 2 });
const pointer3 = new TestObject({ value: 3 });

const obj1 = new TestObject({ pointer: pointer1, name: 'Hello' });
const obj2 = new TestObject({ pointer: pointer2, name: 'Hello' });
const obj3 = new TestObject({ pointer: pointer3, name: 'World' });

const pipeline = [
{
match: { name: 'Hello' },
},
{
// Transform className$objectId to objectId and store in new field tempPointer
project: {
tempPointer: { $substr: ['$_p_pointer', 11, -1] }, // Remove TestObject$
},
},
{
// Left Join, replace objectId stored in tempPointer with an actual object
lookup: {
from: 'TestObject',
localField: 'tempPointer',
foreignField: '_id',
as: 'tempPointer',
},
},
{
// lookup returns an array, Deconstructs an array field to objects
unwind: {
path: '$tempPointer',
},
},
{
match: { 'tempPointer.value': 2 },
},
];
await Parse.Object.saveAll([pointer1, pointer2, pointer3, obj1, obj2, obj3]);

const query = new Parse.Query(TestObject);
query.distinct('score').then((results) => {
const results = await query.aggregate(pipeline);

expect(results.length).toEqual(1);
expect(results[0].tempPointer.value).toEqual(2);
});

it('aggregate pipeline on top of a simple query', async done => {
const pipeline = {
group: { objectId: '$name' },
};
let results = await new Parse.Query(TestObject).equalTo('name', 'foo').aggregate(pipeline);

expect(results.length).toBe(1);

results = await new Parse.Query(TestObject).equalTo('score', 20).aggregate(pipeline);

expect(results.length).toBe(1);

done();
});

it('distinct query', () => {
const query = new Parse.Query(TestObject);
return query.distinct('score').then(results => {
assert.equal(results.length, 2);
assert.equal(results[0], 10);
assert.equal(results[1], 20);
done();
}).catch(done.fail);
// Order the results in case
const orderedResults = results.sort((a, b) => a - b);
assert.equal(orderedResults[0], 10);
assert.equal(orderedResults[1], 20);
});
});

it('distinct equalTo query', (done) => {
it('distinct equalTo query', done => {
const query = new Parse.Query(TestObject);
query.equalTo('name', 'foo')
query.distinct('score').then((results) => {
query.equalTo('name', 'foo');
query.distinct('score').then(results => {
assert.equal(results.length, 1);
assert.equal(results[0], 10);
done();
2,976 changes: 1,961 additions & 1,015 deletions integration/test/ParseQueryTest.js

Large diffs are not rendered by default.

412 changes: 230 additions & 182 deletions integration/test/ParseRelationTest.js

Large diffs are not rendered by default.

33 changes: 0 additions & 33 deletions integration/test/ParseRoleTest.js

This file was deleted.

564 changes: 440 additions & 124 deletions integration/test/ParseSchemaTest.js

Large diffs are not rendered by default.

34 changes: 34 additions & 0 deletions integration/test/ParseServerTest.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
'use strict';

const assert = require('assert');

describe('ParseServer', () => {
it('can reconfigure server', async done => {
const parseServer = await reconfigureServer({ serverURL: 'www.google.com' });
assert.strictEqual(parseServer.config.serverURL, 'www.google.com');
parseServer.server.close(async () => {
await reconfigureServer();
done();
});
});

it('can shutdown', async done => {
const parseServer = await reconfigureServer();
const object = new TestObject({ foo: 'bar' });
await parseServer.handleShutdown();
parseServer.server.close(async () => {
try {
await object.save();
} catch (e) {
assert.strictEqual(
e.message,
'XMLHttpRequest failed: "Unable to connect to the Parse API"'
);
await reconfigureServer({});
await object.save();
assert(object.id);
done();
}
});
});
});
285 changes: 157 additions & 128 deletions integration/test/ParseSubclassTest.js
Original file line number Diff line number Diff line change
@@ -1,187 +1,216 @@
'use strict';

const assert = require('assert');
const clear = require('./clear');
const mocha = require('mocha');
const Parse = require('../../node');

describe('Parse Object Subclasses', () => {
before((done) => {
Parse.initialize('integration', null, 'notsosecret');
Parse.CoreManager.set('SERVER_URL', 'http://localhost:1337/parse');
Parse.Storage._clear();
clear().then(() => {
done();
});
});

it('uses subclasses when doing query find', (done) => {
let Subclass = Parse.Object.extend('Subclass', {
it('uses subclasses when doing query find', done => {
const Subclass = Parse.Object.extend('Subclass', {
initialize(attributes, options, number) {
this.number = number || -1;
}
},
});

let object = new Subclass({}, {}, 57);
const object = new Subclass({}, {}, 57);
assert.equal(object.number, 57);
object.save().then(() => {
let query = new Parse.Query(Subclass);
return query.find();
}).then((results) => {
assert.equal(results.length, 1);
assert(results[0] instanceof Subclass);
assert.equal(results[0].number, -1);
done();
});
object
.save()
.then(() => {
const query = new Parse.Query(Subclass);
return query.find();
})
.then(results => {
assert.equal(results.length, 1);
assert(results[0] instanceof Subclass);
assert.equal(results[0].number, -1);
done();
});
});

it('uses subclasses when doing query get', (done) => {
let Subclass = Parse.Object.extend('Subclass', {
it('uses subclasses when doing query get', done => {
const Subclass = Parse.Object.extend('Subclass', {
initialize(attributes, options, number) {
this.number = number || -1;
}
},
});

let object = new Subclass({}, {}, 57);
const object = new Subclass({}, {}, 57);
assert.equal(object.number, 57);
object.save().then(() => {
let query = new Parse.Query(Subclass);
return query.get(object.id);
}).then((result) => {
assert(result instanceof Subclass);
assert.equal(result.number, -1);
done();
});
object
.save()
.then(() => {
const query = new Parse.Query(Subclass);
return query.get(object.id);
})
.then(result => {
assert(result instanceof Subclass);
assert.equal(result.number, -1);
done();
});
});

it('uses subclasses with array results', (done) => {
let Container = Parse.Object.extend('Container');
let Item = Parse.Object.extend('Item');
let ItemChild = Parse.Object.extend('Item');
let ItemGrandchild = ItemChild.extend();

let item = new Item();
item.save({ foo: 'bar' }).then(() => {
let container = new Container();
return container.save({ items: [item] });
}).then((container) => {
let query = new Parse.Query(Container);
return query.get(container.id);
}).then((container) => {
assert(container instanceof Container);
assert.equal(container.get('items').length, 1);
let item = container.get('items')[0];
assert(item instanceof Item);
assert(item instanceof ItemChild);
assert(item instanceof ItemGrandchild);
done();
});
it('uses subclasses with array results', done => {
const Container = Parse.Object.extend('Container');
const Item = Parse.Object.extend('Item');
const ItemChild = Parse.Object.extend('Item');
const ItemGrandchild = ItemChild.extend();

const item = new Item();
item
.save({ foo: 'bar' })
.then(() => {
const container = new Container();
return container.save({ items: [item] });
})
.then(container => {
const query = new Parse.Query(Container);
return query.get(container.id);
})
.then(container => {
assert(container instanceof Container);
assert.equal(container.get('items').length, 1);
const item = container.get('items')[0];
assert(item instanceof Item);
assert(item instanceof ItemChild);
assert(item instanceof ItemGrandchild);
done();
});
});

it('can subclass multiple levels explicitly', (done) => {
let Parent = Parse.Object.extend('MyClass', {
it('can subclass multiple levels explicitly', done => {
const Parent = Parse.Object.extend('MyClass', {
initialize() {
Parent.__super__.initialize.apply(this, arguments);
this.parent = true;
}
},
});

let Child = Parent.extend({
const Child = Parent.extend({
initialize() {
Child.__super__.initialize.apply(this, arguments);
this.child = true;
}
},
});

let Grandchild = Child.extend({
const Grandchild = Child.extend({
initialize() {
Grandchild.__super__.initialize.apply(this, arguments);
this.grandchild = true;
}
});

let object = new Parent();

object.save().then(() => {
let query = new Parse.Query(Grandchild);
return query.get(object.id);
}).then((result) => {
assert(result instanceof Parent);
assert(result instanceof Child);
assert(result instanceof Grandchild);
assert(result.parent);
assert(result.child);
assert(result.grandchild);
done();
});
},
});

const object = new Parent();

object
.save()
.then(() => {
const query = new Parse.Query(Grandchild);
return query.get(object.id);
})
.then(result => {
assert(result instanceof Parent);
assert(result instanceof Child);
assert(result instanceof Grandchild);
assert(result.parent);
assert(result.child);
assert(result.grandchild);
done();
});
});

it('can subclass multiple levels implicitly', (done) => {
let Parent = Parse.Object.extend('MyClass', {
it('can subclass multiple levels implicitly', done => {
const Parent = Parse.Object.extend('MyClass', {
initialize() {
Parent.__super__.initialize.apply(this, arguments);
this.parent = true;
}
},
});

let Child = Parse.Object.extend('MyClass', {
const Child = Parse.Object.extend('MyClass', {
initialize() {
Child.__super__.initialize.apply(this, arguments);
this.child = true;
}
},
});

let Grandchild = Parse.Object.extend('MyClass', {
const Grandchild = Parse.Object.extend('MyClass', {
initialize() {
Grandchild.__super__.initialize.apply(this, arguments);
this.grandchild = true;
}
});

let object = new Parent();

object.save().then(() => {
let query = new Parse.Query(Grandchild);
return query.get(object.id);
}).then((result) => {
assert(result instanceof Parent);
assert(result instanceof Child);
assert(result instanceof Grandchild);
assert(result.parent);
assert(result.child);
assert(result.grandchild);
done();
});
},
});

const object = new Parent();

object
.save()
.then(() => {
const query = new Parse.Query(Grandchild);
return query.get(object.id);
})
.then(result => {
assert(result instanceof Parent);
assert(result instanceof Child);
assert(result instanceof Grandchild);
assert(result.parent);
assert(result.child);
assert(result.grandchild);
done();
});
});

it('can subclass multiple levels explicitly with different names', (done) => {
let Parent = Parse.Object.extend('MyClass');
let Child = Parent.extend();
let Grandchild = Child.extend('NewClass');

let object = new Parent();

object.save().then(() => {
let query = new Parse.Query(Child);
return query.get(object.id);
}).then((result) => {
assert(result instanceof Parent);
assert(result instanceof Child);

let query = new Parse.Query(Grandchild);
return query.get(object.id);
}).then(null, () => {
// No object found
done();
});
it('can subclass multiple levels explicitly with different names', done => {
const Parent = Parse.Object.extend('MyClass');
const Child = Parent.extend();
const Grandchild = Child.extend('NewClass');

const object = new Parent();

object
.save()
.then(() => {
const query = new Parse.Query(Child);
return query.get(object.id);
})
.then(result => {
assert(result instanceof Parent);
assert(result instanceof Child);

const query = new Parse.Query(Grandchild);
return query.get(object.id);
})
.then(null, () => {
// No object found
done();
});
});

it('propagates instance properties', () => {
let Squirtle = Parse.Object.extend('Squirtle', {
water: true
const Squirtle = Parse.Object.extend('Squirtle', {
water: true,
});
var Wartortle = Squirtle.extend('Wartortle');
let wartortle = new Wartortle();
const Wartortle = Squirtle.extend('Wartortle');
const wartortle = new Wartortle();
assert(wartortle.water);
});

it('registerSubclass with unknown className', async () => {
Parse.Object.unregisterSubclass('TestObject');
let outerClassName = '';
class TestObject extends Parse.Object {
constructor(className) {
super(className);
outerClassName = className;
}
}
Parse.Object.registerSubclass('TestObject', TestObject);
const o = new Parse.Object('TestObject');
await o.save();
const query = new Parse.Query('TestObject');
const first = await query.first();
expect(first instanceof TestObject).toBe(true);
expect(first.className).toBe('TestObject');
expect(outerClassName).toBe('TestObject');
Parse.Object.unregisterSubclass('TestObject');
});
});
Loading